[go: nahoru, domu]

Merge "Remember the graph that is passed to a NavHost" into androidx-master-dev
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index d44d46c..3fd78ec 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -49,30 +49,28 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
-
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
       - name: "Upload build artifacts"
         continue-on-error: true
@@ -104,30 +102,28 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
-
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
       - name: "Upload build artifacts"
         continue-on-error: true
@@ -159,30 +155,28 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
-
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
       - name: "Upload build artifacts"
         continue-on-error: true
@@ -214,30 +208,28 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
-
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
       - name: "Upload build artifacts"
         continue-on-error: true
@@ -269,32 +261,31 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
+
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
 
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
-
-      - name: "Upload build artifacts"
+      - name: "upload build artifacts"
         continue-on-error: true
         if: always()
         uses: actions/upload-artifact@v2
@@ -324,30 +315,28 @@
         with:
           fetch-depth: 1
 
-      - name: "Cache ~/.gradle/caches"
-        uses: actions/cache@v2.1.2
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
         with:
-          path: "~/.gradle/caches"
-          key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
-          restore-keys: |
-            gradle-cache-${{ runner.os }}-
+          java-version: "11"
 
-      - name: "Cache ~/.gradlew/wrapper"
-        uses: actions/cache@v2.1.2
-        with:
-          path: ~/.gradle/wrapper
-          key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+      - name: "Set environment variables"
+        shell: bash
+        run: |
+          set -x
+          echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+          echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
 
-      - name: "Cache ~/.konan"
-        uses: actions/cache@v2.1.2
+      - name: "./gradlew buildOnServer"
+        uses: eskatos/gradle-command-action@v1
+        env:
+          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
         with:
-          path: ~/.konan
-          key: konan-${{ runner.os }}
-
-      - name: "Build"
-        uses: androidx/build-on-server-action@main
-        with:
-          path: ${{ env.group-id }}
+          arguments: buildOnServer
+          build-root-directory: ${{ env.group-id }}
+          gradle-executable: ${{ env.group-id }}/gradlew
+          wrapper-directory: ${{ env.group-id }}/gradle/wrapper
 
       - name: "Upload build artifacts"
         continue-on-error: true
diff --git a/activity/activity/src/androidTest/AndroidManifest.xml b/activity/activity/src/androidTest/AndroidManifest.xml
index 13d2e2e..8ad02b8 100644
--- a/activity/activity/src/androidTest/AndroidManifest.xml
+++ b/activity/activity/src/androidTest/AndroidManifest.xml
@@ -29,6 +29,7 @@
         <activity android:name="androidx.activity.EmptyContentActivity" />
         <activity android:name="androidx.activity.AutoRestarterActivity"/>
         <activity android:name="androidx.activity.ResultComponentActivity"/>
+        <activity android:name="androidx.activity.ResumeViewModelActivity" />
     </application>
 
 </manifest>
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
index ea204b3..75b5142 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
@@ -97,6 +97,16 @@
         assertThat(androidModel.cleared).isTrue()
         assertThat(savedStateModel.cleared).isTrue()
     }
+
+    @Test
+    fun testViewModelsAfterOnResume() {
+        val scenario = ActivityScenario.launch(ResumeViewModelActivity::class.java)
+        with(scenario) {
+            val vm = withActivity { viewModel }
+            recreate()
+            assertThat(withActivity { viewModel }).isSameInstanceAs(vm)
+        }
+    }
 }
 
 class ViewModelActivity : ComponentActivity() {
@@ -149,3 +159,12 @@
         cleared = true
     }
 }
+
+class ResumeViewModelActivity : ComponentActivity() {
+    lateinit var viewModel: TestViewModel
+
+    override fun onResume() {
+        super.onResume()
+        viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
+    }
+}
\ No newline at end of file
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index a4f7513..068fe6e 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -260,6 +260,14 @@
                 }
             }
         });
+        getLifecycle().addObserver(new LifecycleEventObserver() {
+            @Override
+            public void onStateChanged(@NonNull LifecycleOwner source,
+                    @NonNull Lifecycle.Event event) {
+                ensureViewModelStore();
+                getLifecycle().removeObserver(this);
+            }
+        });
 
         if (19 <= SDK_INT && SDK_INT <= 23) {
             getLifecycle().addObserver(new ImmLeaksCleaner(this));
@@ -472,6 +480,12 @@
             throw new IllegalStateException("Your activity is not yet attached to the "
                     + "Application instance. You can't request ViewModel before onCreate call.");
         }
+        ensureViewModelStore();
+        return mViewModelStore;
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    void ensureViewModelStore() {
         if (mViewModelStore == null) {
             NonConfigurationInstances nc =
                     (NonConfigurationInstances) getLastNonConfigurationInstance();
@@ -483,7 +497,6 @@
                 mViewModelStore = new ViewModelStore();
             }
         }
-        return mViewModelStore;
     }
 
     /**
diff --git a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
index 5f8aea8..da3c31d 100644
--- a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
+++ b/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <!-- Important: disable debuggable for accurate performance results -->
     <application
         android:name=".AdsIdentifierBenchmarkApplication"
+        android:requestLegacyExternalStorage="true"
         android:debuggable="false"
         tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/appcompat/appcompat-benchmark/src/androidTest/AndroidManifest.xml b/appcompat/appcompat-benchmark/src/androidTest/AndroidManifest.xml
index 1c5fab4..992ce4e 100644
--- a/appcompat/appcompat-benchmark/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat-benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
index 080cf76..09ac4df 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
@@ -16,6 +16,8 @@
 
 package androidx.appsearch.app;
 
+import static androidx.appsearch.app.AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
+import static androidx.appsearch.app.AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN;
 import static androidx.appsearch.app.AppSearchTestUtils.checkIsBatchResultSuccess;
 import static androidx.appsearch.app.AppSearchTestUtils.checkIsResultSuccess;
 import static androidx.appsearch.app.AppSearchTestUtils.convertSearchResultsToDocuments;
@@ -51,6 +53,27 @@
     }
 
     @AppSearchDocument
+    static class Card {
+        @AppSearchDocument.Uri String mUri;
+        @AppSearchDocument.Property
+                (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
+        String mString;        // 3a
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof Card)) {
+                return false;
+            }
+            Card otherCard = (Card) other;
+            assertThat(otherCard.mUri).isEqualTo(this.mUri);
+            return true;
+        }
+    }
+
+    @AppSearchDocument
     static class Gift {
         @AppSearchDocument.Uri String mUri;
 
@@ -62,7 +85,7 @@
         @AppSearchDocument.Property Collection<Boolean> mCollectBoolean;   // 1a
         @AppSearchDocument.Property Collection<byte[]> mCollectByteArr;    // 1a
         @AppSearchDocument.Property Collection<String> mCollectString;     // 1b
-        @AppSearchDocument.Property Collection<Gift> mCollectGift;         // 1c
+        @AppSearchDocument.Property Collection<Card> mCollectCard;         // 1c
 
         // Arrays
         @AppSearchDocument.Property Long[] mArrBoxLong;         // 2a
@@ -78,7 +101,7 @@
         @AppSearchDocument.Property byte[][] mArrUnboxByteArr;  // 2b
         @AppSearchDocument.Property Byte[] mBoxByteArr;         // 2a
         @AppSearchDocument.Property String[] mArrString;        // 2b
-        @AppSearchDocument.Property Gift[] mArrGift;            // 2c
+        @AppSearchDocument.Property Card[] mArrCard;            // 2c
 
         // Single values
         @AppSearchDocument.Property String mString;        // 3a
@@ -93,7 +116,7 @@
         @AppSearchDocument.Property Boolean mBoxBoolean;   // 3a
         @AppSearchDocument.Property boolean mUnboxBoolean; // 3b
         @AppSearchDocument.Property byte[] mUnboxByteArr;  // 3a
-        @AppSearchDocument.Property Gift mGift;            // 3c
+        @AppSearchDocument.Property Card mCard;            // 3c
 
         @Override
         public boolean equals(Object other) {
@@ -118,7 +141,7 @@
             assertThat(otherGift.mArrUnboxFloat).isEqualTo(this.mArrUnboxFloat);
             assertThat(otherGift.mArrUnboxLong).isEqualTo(this.mArrUnboxLong);
             assertThat(otherGift.mArrUnboxInt).isEqualTo(this.mArrUnboxInt);
-            assertThat(otherGift.mArrGift).isEqualTo(this.mArrGift);
+            assertThat(otherGift.mArrCard).isEqualTo(this.mArrCard);
 
             assertThat(otherGift.mCollectLong).isEqualTo(this.mCollectLong);
             assertThat(otherGift.mCollectInteger).isEqualTo(this.mCollectInteger);
@@ -126,7 +149,7 @@
             assertThat(otherGift.mCollectString).isEqualTo(this.mCollectString);
             assertThat(otherGift.mCollectDouble).isEqualTo(this.mCollectDouble);
             assertThat(otherGift.mCollectFloat).isEqualTo(this.mCollectFloat);
-            assertThat(otherGift.mCollectGift).isEqualTo(this.mCollectGift);
+            assertThat(otherGift.mCollectCard).isEqualTo(this.mCollectCard);
             checkCollectByteArr(otherGift.mCollectByteArr, this.mCollectByteArr);
 
             assertThat(otherGift.mString).isEqualTo(this.mString);
@@ -141,7 +164,7 @@
             assertThat(otherGift.mBoxBoolean).isEqualTo(this.mBoxBoolean);
             assertThat(otherGift.mUnboxBoolean).isEqualTo(this.mUnboxBoolean);
             assertThat(otherGift.mUnboxByteArr).isEqualTo(this.mUnboxByteArr);
-            assertThat(otherGift.mGift).isEqualTo(this.mGift);
+            assertThat(otherGift.mCard).isEqualTo(this.mCard);
             return true;
         }
 
@@ -160,7 +183,7 @@
         //TODO(b/156296904) add test for int, float, GenericDocument, and class with
         // @AppSearchDocument annotation
         checkIsResultSuccess(mSession.setSchema(
-                new SetSchemaRequest.Builder().addDataClass(Gift.class).build()));
+                new SetSchemaRequest.Builder().addDataClass(Card.class, Gift.class).build()));
 
         // Create a Gift object and assign values.
         Gift inputDataClass = new Gift();
@@ -180,11 +203,11 @@
         inputDataClass.mArrUnboxInt = new int[]{5, 4};
         inputDataClass.mArrUnboxLong = new long[]{7, 6};
 
-        Gift innerGift1 = new Gift();
-        innerGift1.mUri = "innerGift.uri1";
-        Gift innerGift2 = new Gift();
-        innerGift2.mUri = "innerGift.uri2";
-        inputDataClass.mArrGift = new Gift[]{innerGift1, innerGift2};
+        Card card1 = new Card();
+        card1.mUri = "card.uri1";
+        Card card2 = new Card();
+        card2.mUri = "card.uri2";
+        inputDataClass.mArrCard = new Card[]{card2, card2};
 
         inputDataClass.mCollectLong = Arrays.asList(inputDataClass.mArrBoxLong);
         inputDataClass.mCollectInteger = Arrays.asList(inputDataClass.mArrBoxInteger);
@@ -193,7 +216,7 @@
         inputDataClass.mCollectDouble = Arrays.asList(inputDataClass.mArrBoxDouble);
         inputDataClass.mCollectFloat = Arrays.asList(inputDataClass.mArrBoxFloat);
         inputDataClass.mCollectByteArr = Arrays.asList(inputDataClass.mArrUnboxByteArr);
-        inputDataClass.mCollectGift = Arrays.asList(innerGift1, innerGift2);
+        inputDataClass.mCollectCard = Arrays.asList(card2, card2);
 
         inputDataClass.mString = "String";
         inputDataClass.mBoxLong = 1L;
@@ -207,7 +230,7 @@
         inputDataClass.mBoxBoolean = true;
         inputDataClass.mUnboxBoolean = false;
         inputDataClass.mUnboxByteArr = new byte[]{1, 2, 3};
-        inputDataClass.mGift = innerGift1;
+        inputDataClass.mCard = card1;
 
         // Index the Gift document and query it.
         checkIsBatchResultSuccess(mSession.putDocuments(
@@ -231,7 +254,7 @@
     public void testAnnotationProcessor_QueryByType() throws Exception {
         checkIsResultSuccess(mSession.setSchema(
                 new SetSchemaRequest.Builder()
-                        .addDataClass(Gift.class)
+                        .addDataClass(Card.class, Gift.class)
                         .addSchema(AppSearchEmail.SCHEMA).build()));
 
         // Create documents and index them
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
index 14f4e33..48129c8 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
@@ -152,8 +152,8 @@
                 .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
                         .setDataType(PropertyConfig.DATA_TYPE_INT64)
                         .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(PropertyConfig.INDEXING_TYPE_NONE)
+                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_NONE)
                         .build())
                 .build();
         checkIsResultSuccess(
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
index e0c657b..2a60798 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
@@ -39,7 +39,6 @@
 public class GlobalSearchSessionTest {
     private AppSearchSession mDb1;
     private AppSearchSession mDb2;
-    private AppSearchSession mDefaultDb;
 
     private GlobalSearchSession mGlobalAppSearchManager;
 
@@ -52,8 +51,6 @@
         mDb2 = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
                         .setDatabaseName("testDb2").build()));
-        mDefaultDb = checkIsResultSuccess(LocalStorage.createSearchSession(
-                new LocalStorage.SearchContext.Builder(context).build()));
 
         mGlobalAppSearchManager = checkIsResultSuccess(LocalStorage.createGlobalSearchSession(
                 new LocalStorage.GlobalSearchContext.Builder(context).build()));
@@ -63,8 +60,6 @@
                 mDb1.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
         checkIsResultSuccess(
                 mDb2.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
-        checkIsResultSuccess(mDefaultDb.setSchema(
-                        new SetSchemaRequest.Builder().setForceOverride(true).build()));
     }
 
     @Test
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
index ecb2b9e..1f1b399 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
@@ -191,6 +191,7 @@
          * <p>If not specified, defaults to {@link
          * AppSearchSchema.PropertyConfig#INDEXING_TYPE_NONE} (the field will not be indexed and
          * cannot be queried).
+         * TODO(b/171857731) renamed to TermMatchType when using String-specific indexing config.
          */
         @AppSearchSchema.PropertyConfig.IndexingType int indexingType()
                 default AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
index 0bb8c52..ff81acd 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
@@ -17,6 +17,7 @@
 package androidx.appsearch.app;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -80,6 +81,32 @@
     ListenableFuture<AppSearchResult<Void>> setSchema(@NonNull SetSchemaRequest request);
 
     /**
+     * Sets visibility settings for documents in AppSearch.
+     *
+     * <p>Visibility settings are not carried over from previous {@code SetVisibilityRequest}s.
+     * The entire set of visibility settings must be specified on each {@code SetVisibilityRequest}.
+     *
+     * <p>The visibility settings apply to the schema instance that currently exists. If a schema
+     * is deleted and then re-added, the visibility setting will no longer apply to the new
+     * instance of the schema.
+     *
+     * <p>An {@link AppSearchResult#RESULT_NOT_FOUND} will be returned if a specified schema
+     * doesn't exist.
+     *
+     * <p>The default visibility settings are that all documents can be shown on platform
+     * surfaces. Documents can be opted out of being shown on platform surfaces by specifying
+     * their schema type in {@link SetVisibilityRequest.Builder#addHiddenFromPlatformSurfaces}.
+     *
+     * @param request The visibility settings request
+     * @return The pending result of performing this operation.
+     * @hide
+     */
+    // TODO(b/169883602): Add integration tests for this API in the platform.
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    ListenableFuture<AppSearchResult<Void>> setVisibility(@NonNull SetVisibilityRequest request);
+
+    /**
      * Indexes documents into AppSearch.
      *
      * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
@@ -178,9 +205,9 @@
      * the current database.
      *
      * @param queryExpression Query String to search.
-     * @param searchSpec Spec containing schemaTypes, namespaces and query expression
-     *                   indicates how document will be removed. All specific about how to
-     *                   scoring, ordering, snippeting and resulting will be ignored.
+     * @param searchSpec      Spec containing schemaTypes, namespaces and query expression
+     *                        indicates how document will be removed. All specific about how to
+     *                        scoring, ordering, snippeting and resulting will be ignored.
      * @return The pending result of performing this operation.
      */
     @NonNull
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetVisibilityRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetVisibilityRequest.java
index 09e5a13..291a735 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetVisibilityRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetVisibilityRequest.java
@@ -26,22 +26,9 @@
 import java.util.Set;
 
 /**
- * Encapsulates a request to update the visibility settings of an {@link AppSearchManager} database.
+ * Encapsulates a request to update the visibility settings of an {@link AppSearchSession} database.
  *
- * // TODO(b/169883602): Move these comments to the actual setVisibilityRequest(request) API.
- * <p>Visibility settings are not carried over from previous {@code SetVisibilityRequest}s. The
- * entire set of visibility settings must be specified on each {@code SetVisibilityRequest}.
- *
- * <p>The visibility settings apply to the schema instance that currently exists. If a schema is
- * deleted and then re-added, the visibility setting will no longer apply to the new instance of
- * the schema.
- *
- * <p>An {@link AppSearchException} will be thrown if a specified schema doesn't exist.
- *
- * <p>The default visibility settings are that all documents can be shown on platform surfaces.
- * Documents can be opted out of being shown on platform surfaces by specifying their schema type
- * in {@link SetVisibilityRequest.Builder#setHiddenFromPlatformSurfaces}.
- *
+ * @see AppSearchSession#setVisibility
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -66,14 +53,14 @@
 
         /** Set documents of type {@code schemas} to be hidden from platform surfaces. */
         @NonNull
-        public Builder setHiddenFromPlatformSurfaces(@NonNull AppSearchSchema... schemas) {
+        public Builder addHiddenFromPlatformSurfaces(@NonNull AppSearchSchema... schemas) {
             Preconditions.checkNotNull(schemas);
-            return setHiddenFromPlatformSurfaces(Arrays.asList(schemas));
+            return addHiddenFromPlatformSurfaces(Arrays.asList(schemas));
         }
 
         /** Set documents of type {@code schemas} to be hidden from platform surfaces. */
         @NonNull
-        public Builder setHiddenFromPlatformSurfaces(@NonNull Collection<AppSearchSchema> schemas) {
+        public Builder addHiddenFromPlatformSurfaces(@NonNull Collection<AppSearchSchema> schemas) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkNotNull(schemas);
             mSchemasHiddenFromPlatformSurfaces.addAll(schemas);
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
index 0af8dc1..eca9c5a 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
@@ -188,6 +188,10 @@
 
         // Find tokenizer type
         int tokenizerType = Integer.parseInt(params.get("tokenizerType").toString());
+        if (Integer.parseInt(params.get("indexingType").toString()) == 0) {
+            //TODO(b/171857731) remove this hack after apply to Icing lib's change.
+            tokenizerType = 0;
+        }
         ClassName tokenizerEnum;
         if (tokenizerType == 0 || isPropertyDocument) {  // TOKENIZER_TYPE_NONE
             //It is only valid for tokenizer_type to be 'NONE' if the data type is
diff --git a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
index 86f579d..94a5317 100644
--- a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
+++ b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
@@ -454,7 +454,8 @@
                         + "@AppSearchDocument\n"
                         + "public class Gift {\n"
                         + "  @AppSearchDocument.Uri String uri;\n"
-                        + "  @AppSearchDocument.Property(tokenizerType=100) String str;\n"
+                        + "  @AppSearchDocument.Property(indexingType=1, tokenizerType=100)\n"
+                        + "  String str;\n"
                         + "}\n");
         CompilationSubject.assertThat(compilation).hadErrorContaining("Unknown tokenizer type 100");
     }
@@ -485,7 +486,8 @@
                         + "@AppSearchDocument\n"
                         + "public class Gift {\n"
                         + "  @AppSearchDocument.Uri String uri;\n"
-                        + "  @AppSearchDocument.Property(indexingType=100) String str;\n"
+                        + "  @AppSearchDocument.Property(indexingType=100, tokenizerType=1)\n"
+                        + "  String str;\n"
                         + "}\n");
         CompilationSubject.assertThat(compilation).hadErrorContaining("Unknown indexing type 100");
     }
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
index 4dfb30e..b256fa5 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
@@ -26,43 +26,43 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("stringProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("integerProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("longProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("floatProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("doubleProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("booleanProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("bytesProp")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
index f9bea13..e315345 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
index c30fa56..1cf9253 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
index 50f7553..13f4f18 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
@@ -24,25 +24,25 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatReq")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatNoReq")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("req")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("noReq")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
index 7855d50..87283d4 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("indexNone")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("indexExact")
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
index 8a91767..8900092 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrString")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
index 45459ae..d843153 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("newName")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
index 8a6e8f6b..890d43d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
index 9f0d759..be28a52 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
@@ -26,25 +26,25 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("listOfString")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("setOfInt")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatedByteArray")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("byteArray")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
index e4d80c9..8499df6 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
@@ -21,19 +21,19 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("cat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("dog")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
index 9692936..d3ee29b 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
@@ -32,43 +32,43 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectLong")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectInteger")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectDouble")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectFloat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectBoolean")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectByteArr")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectString")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("collectGift")
@@ -81,79 +81,79 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxLong")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxLong")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxInteger")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxInt")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxDouble")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxDouble")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxFloat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxFloat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxBoolean")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxBoolean")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxByteArr")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxByteArr")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrString")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("arrGift")
@@ -166,73 +166,73 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("string")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxLong")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxLong")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxInteger")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxInt")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxDouble")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxDouble")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxFloat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxFloat")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("boxBoolean")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxBoolean")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxByteArr")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("gift")
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
index 71ec562..9be6cdb 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
@@ -27,7 +27,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("tokPlain")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
index 8a6e8f6b..890d43d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
@@ -21,7 +21,7 @@
           .addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
             .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+            .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
             .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
             .build())
           .build();
diff --git a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 6605f68..82be96d 100644
--- a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -25,6 +25,7 @@
 import androidx.appsearch.app.SearchResultPage;
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.localstorage.converter.SchemaToProtoConverter;
 
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.GetOptimizeInfoResultProto;
@@ -42,18 +43,33 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 public class AppSearchImplTest {
     @Rule
     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
     private AppSearchImpl mAppSearchImpl;
+    private SchemaTypeConfigProto mVisibilitySchemaProto;
 
     @Before
     public void setUp() throws Exception {
         mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+
+        AppSearchSchema visibilityAppSearchSchema =
+                new AppSearchSchema.Builder(
+                        VisibilityStore.DATABASE_NAME + AppSearchImpl.DATABASE_DELIMITER
+                                + VisibilityStore.SCHEMA_TYPE)
+                        .addProperty(new AppSearchSchema.PropertyConfig.Builder(
+                                VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
+                                .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                .build())
+                        .build();
+        mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema);
     }
 
     /**
@@ -62,8 +78,14 @@
      * schema.
      */
     @Test
-    public void testRewriteSchema() throws Exception {
-        SchemaProto.Builder existingSchemaBuilder = mAppSearchImpl.getSchemaProto().toBuilder();
+    public void testRewriteSchema_AddType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("existingDatabase/Foo").build());
+
+        // Create a copy so we can modify it.
+        List<SchemaTypeConfigProto> existingTypes =
+                new ArrayList<>(existingSchemaBuilder.getTypesList());
 
         SchemaProto newSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
@@ -90,15 +112,20 @@
                         ).build()
                 ).build();
 
-        Set<String> newTypes = mAppSearchImpl.rewriteSchema("databaseName", existingSchemaBuilder,
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
+                "newDatabase", existingSchemaBuilder,
                 newSchema);
-        assertThat(newTypes).containsExactly("databaseName/Foo", "databaseName/TestType");
+
+        // We rewrote all the new types that were added. And nothing was removed.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("newDatabase/Foo", "newDatabase/TestType");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
 
         SchemaProto expectedSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("databaseName/Foo").build())
+                        .setSchemaType("newDatabase/Foo").build())
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("databaseName/TestType")
+                        .setSchemaType("newDatabase/TestType")
                         .addProperties(PropertyConfigProto.newBuilder()
                                 .setPropertyName("subject")
                                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
@@ -114,10 +141,75 @@
                                 .setPropertyName("link")
                                 .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
                                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setSchemaType("databaseName/RefType")
+                                .setSchemaType("newDatabase/RefType")
                                 .build()
                         ).build())
                 .build();
+
+        existingTypes.addAll(expectedSchema.getTypesList());
+        assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes);
+    }
+
+    /**
+     * Ensure that we track all types that were rewritten in the input schema. Even if they were
+     * not technically "added" to the existing schema.
+     */
+    @Test
+    public void testRewriteSchema_RewriteType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("existingDatabase/Foo").build());
+
+        SchemaProto newSchema = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("Foo").build())
+                .build();
+
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
+                "existingDatabase", existingSchemaBuilder, newSchema);
+
+        // Nothing was removed, but the method did rewrite the type name.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("existingDatabase/Foo");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+
+        // Same schema since nothing was added.
+        SchemaProto expectedSchema = existingSchemaBuilder.build();
+        assertThat(existingSchemaBuilder.getTypesList())
+                .containsExactlyElementsIn(expectedSchema.getTypesList());
+    }
+
+    /**
+     * Ensure that we track which types from the existing schema are deleted when a new schema is
+     * set.
+     */
+    @Test
+    public void testRewriteSchema_DeleteType() throws Exception {
+        SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("existingDatabase/Foo").build());
+
+        SchemaProto newSchema = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("Bar").build())
+                .build();
+
+        AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
+                "existingDatabase", existingSchemaBuilder, newSchema);
+
+        // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
+        // new schema.
+        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
+                .containsExactly("existingDatabase/Bar");
+        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes)
+                .containsExactly("existingDatabase/Foo");
+
+        // Same schema since nothing was added.
+        SchemaProto expectedSchema = SchemaProto.newBuilder()
+                .addTypes(SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("existingDatabase/Bar").build())
+                .build();
+
         assertThat(existingSchemaBuilder.getTypesList())
                 .containsExactlyElementsIn(expectedSchema.getTypesList());
     }
@@ -154,7 +246,7 @@
     }
 
     @Test
-    public void testRemoveDocumentTypePrefixes() {
+    public void testRemoveDocumentTypePrefixes() throws Exception {
         DocumentProto insideDocument = DocumentProto.newBuilder()
                 .setUri("inside-uri")
                 .setSchema("databaseName1/type")
@@ -326,12 +418,37 @@
         // Set schema Email to AppSearch database1
         mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
+        // Create expected schemaType proto.
+        SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
                 .build();
+
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
         assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+                .containsExactlyElementsIn(expectedTypes);
+    }
+
+    @Test
+    public void testSetSchema_ExistingSchemaRetainsVisibilitySetting() throws Exception {
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "schema1").build()), /*forceOverride=*/false);
+        mAppSearchImpl.setVisibility("database", Set.of("schema1"));
+
+        // "schema1" is platform hidden now
+        assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
+                "database")).containsExactly("database/schema1");
+
+        // Add a new schema, and include the already-existing "schema1"
+        mAppSearchImpl.setSchema("database", Set.of(new AppSearchSchema.Builder(
+                "schema1").build(), new AppSearchSchema.Builder(
+                "schema2").build()), /*forceOverride=*/false);
+
+        // Check that "schema1" is still platform hidden, but "schema2" is the default platform
+        // visible.
+        assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
+                "database")).containsExactly("database/schema1");
     }
 
     @Test
@@ -342,15 +459,18 @@
         // Set schema Email and Document to AppSearch database1
         mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
+        // Create expected schemaType proto.
+        SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
                 .build();
 
         // Check both schema Email and Document saved correctly.
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
         assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+                .containsExactlyElementsIn(expectedTypes);
 
         final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder(
                 "Email").build());
@@ -364,15 +484,19 @@
         mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/true);
 
         // Check Document schema is removed.
-        exceptedProto = SchemaProto.newBuilder()
+        expectedProto = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
                 .build();
+
+        expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
         assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+                .containsExactlyElementsIn(expectedTypes);
     }
 
     @Test
-    public void testRemoveSchema_differentDataBase() throws Exception {
+    public void testRemoveSchema_DifferentDataBase() throws Exception {
         // Create schemas
         Set<AppSearchSchema> schemas = new HashSet<>();
         schemas.add(new AppSearchSchema.Builder("Email").build());
@@ -382,8 +506,8 @@
         mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
         mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
 
-        // Create excepted schemaType proto.
-        SchemaProto exceptedProto = SchemaProto.newBuilder()
+        // Create expected schemaType proto.
+        SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
@@ -391,23 +515,114 @@
                 .build();
 
         // Check Email and Document is saved in database 1 and 2 correctly.
+        List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
         assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+                .containsExactlyElementsIn(expectedTypes);
 
         // Save only Email to database1 this time.
         schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
         mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/true);
 
-        // Create excepted schemaType list, database 1 should only contain Email but database 2
+        // Create expected schemaType list, database 1 should only contain Email but database 2
         // remains in same.
-        exceptedProto = SchemaProto.newBuilder()
+        expectedProto = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
                 .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
                 .build();
 
         // Check nothing changed in database2.
+        expectedTypes = new ArrayList<>();
+        expectedTypes.add(mVisibilitySchemaProto);
+        expectedTypes.addAll(expectedProto.getTypesList());
         assertThat(mAppSearchImpl.getSchemaProto().getTypesList())
-                .containsExactlyElementsIn(exceptedProto.getTypesList());
+                .containsExactlyElementsIn(expectedTypes);
+    }
+
+
+    @Test
+    public void testRemoveSchema_RemovedFromVisibilityStore() throws Exception {
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "schema1").build()), /*forceOverride=*/false);
+        mAppSearchImpl.setVisibility("database", Set.of("schema1"));
+
+        // "schema1" is platform hidden now
+        assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
+                "database")).containsExactly("database/schema1");
+
+        // Remove "schema1" by force overriding
+        mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/true);
+
+        // Check that "schema1" is no longer considered platform hidden
+        assertThat(
+                mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
+
+        // Add "schema1" back, it gets default visibility settings which means it's not platform
+        // hidden.
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "schema1").build()), /*forceOverride=*/false);
+        assertThat(
+                mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
+    }
+
+    @Test
+    public void testSetVisibility_DefaultPlatformVisible() throws Exception {
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "Schema").build()), /*forceOverride=*/false);
+        assertThat(
+                mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas("database")).isEmpty();
+    }
+
+    @Test
+    public void testSetVisibility_PlatformHidden() throws Exception {
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "Schema").build()), /*forceOverride=*/false);
+        mAppSearchImpl.setVisibility("database", Set.of("Schema"));
+        assertThat(mAppSearchImpl.getVisibilityStore().getPlatformHiddenSchemas(
+                "database")).containsExactly("database/Schema");
+    }
+
+    @Test
+    public void testSetVisibility_UnknownSchema() throws Exception {
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "Schema").build()), /*forceOverride=*/false);
+
+        // We'll throw an exception if a client tries to set visibility on a schema we don't know
+        // about.
+        AppSearchException e = assertThrows(AppSearchException.class,
+                () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
+        assertThat(e).hasMessageThat().contains("Unknown schema(s)");
+    }
+
+    @Test
+    public void testHasSchemaType() throws Exception {
+        // Nothing exists yet
+        assertThat(mAppSearchImpl.hasSchemaType("database", "Schema")).isFalse();
+
+        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+                "Schema").build()), /*forceOverride=*/false);
+        assertThat(mAppSearchImpl.hasSchemaType("database", "Schema")).isTrue();
+
+        assertThat(mAppSearchImpl.hasSchemaType("database", "UnknownSchema")).isFalse();
+    }
+
+    @Test
+    public void testGetDatabases() throws Exception {
+        // No client databases exist yet, but the VisibilityStore's does
+        assertThat(mAppSearchImpl.getDatabases()).containsExactly(VisibilityStore.DATABASE_NAME);
+
+        // Has database1
+        mAppSearchImpl.setSchema("database1", Collections.singleton(new AppSearchSchema.Builder(
+                "schema").build()), /*forceOverride=*/false);
+        assertThat(mAppSearchImpl.getDatabases()).containsExactly(
+                VisibilityStore.DATABASE_NAME, "database1");
+
+        // Has both databases
+        mAppSearchImpl.setSchema("database2", Collections.singleton(new AppSearchSchema.Builder(
+                "schema").build()), /*forceOverride=*/false);
+        assertThat(mAppSearchImpl.getDatabases()).containsExactly(
+                VisibilityStore.DATABASE_NAME, "database1", "database2");
     }
 }
diff --git a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
new file mode 100644
index 0000000..a9ada3b
--- /dev/null
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.appsearch.localstorage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class VisibilityStoreTest {
+
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private AppSearchImpl mAppSearchImpl;
+    private VisibilityStore mVisibilityStore;
+
+    @Before
+    public void setUp() throws Exception {
+        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+        mVisibilityStore = mAppSearchImpl.getVisibilityStore();
+    }
+
+    @Test
+    public void testSetVisibility() throws Exception {
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema1", "schema2");
+
+        // New .setVisibility() call completely overrides previous visibility settings. So
+        // "schema1" isn't preserved.
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema1", "schema3");
+
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Collections.emptySet());
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
+    }
+
+    @Test
+    public void testRemoveSchemas() throws Exception {
+        mVisibilityStore.setVisibility(
+                "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
+
+        // Removed just schema1
+        mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+                .containsExactly("schema2");
+
+        // Removed everything now
+        mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2"));
+        assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
+    }
+
+}
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 c703d33..f787b51 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
@@ -34,6 +34,7 @@
 import androidx.appsearch.localstorage.converter.SchemaToProtoConverter;
 import androidx.appsearch.localstorage.converter.SearchResultToProtoConverter;
 import androidx.appsearch.localstorage.converter.SearchSpecToProtoConverter;
+import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
 import com.google.android.icing.IcingSearchEngine;
@@ -104,7 +105,9 @@
 @WorkerThread
 public final class AppSearchImpl {
     private static final String TAG = "AppSearchImpl";
-    private static final char DATABASE_DELIMITER = '/';
+
+    @VisibleForTesting
+    static final char DATABASE_DELIMITER = '/';
 
     @VisibleForTesting
     static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@@ -114,17 +117,28 @@
     static final int CHECK_OPTIMIZE_INTERVAL = 100;
 
     private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
+
+    @GuardedBy("mReadWriteLock")
     private final IcingSearchEngine mIcingSearchEngine;
 
+    @GuardedBy("mReadWriteLock")
+    private final VisibilityStore mVisibilityStore;
+
     // The map contains schemaTypes and namespaces for all database. All values in the map have
-    // been already added database name prefix.
+    // the database name prefix.
+    // TODO(b/172360376): Check if this can be replaced with an ArrayMap
+    @GuardedBy("mReadWriteLock")
     private final Map<String, Set<String>> mSchemaMap = new HashMap<>();
+
+    // TODO(b/172360376): Check if this can be replaced with an ArrayMap
+    @GuardedBy("mReadWriteLock")
     private final Map<String, Set<String>> mNamespaceMap = new HashMap<>();
 
     /**
      * The counter to check when to call {@link #checkForOptimize(boolean)}. The interval is
      * {@link #CHECK_OPTIMIZE_INTERVAL}.
      */
+    @GuardedBy("mReadWriteLock")
     private int mOptimizeIntervalCount = 0;
 
     /**
@@ -134,12 +148,15 @@
     @NonNull
     public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
         Preconditions.checkNotNull(icingDir);
-        return new AppSearchImpl(icingDir);
+        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir);
+        appSearchImpl.initializeVisibilityStore();
+        return appSearchImpl;
     }
 
     private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
         boolean isReset = false;
         mReadWriteLock.writeLock().lock();
+
         try {
             // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
             // than once. It's unnecessary and can be a costly operation.
@@ -156,28 +173,46 @@
                 getAllNamespacesResultProto = mIcingSearchEngine.getAllNamespaces();
                 checkSuccess(getAllNamespacesResultProto.getStatus());
             } catch (AppSearchException e) {
+                Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
                 // Some error. Reset and see if it fixes it.
                 reset();
                 isReset = true;
             }
+
+            // Populate schema map
             for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
                 String qualifiedSchemaType = schema.getSchemaType();
                 addToMap(mSchemaMap, getDatabaseName(qualifiedSchemaType), qualifiedSchemaType);
             }
+
+            // Populate namespace map
             for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
-                addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
+                addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace),
+                        qualifiedNamespace);
             }
+
             // TODO(b/155939114): It's possible to optimize after init, which would reduce the time
             //   to when we're able to serve queries. Consider moving this optimize call out.
             if (!isReset) {
                 checkForOptimize(/* force= */ true);
             }
+
+            mVisibilityStore = new VisibilityStore(this);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
     }
 
     /**
+     * Initialize the visibility store in AppSearchImpl.
+     *
+     * @throws AppSearchException on IcingSearchEngine error.
+     */
+    void initializeVisibilityStore() throws AppSearchException {
+        mVisibilityStore.initialize();
+    }
+
+    /**
      * Updates the AppSearch schema for this app.
      *
      * <p>This method belongs to mutate group.
@@ -190,26 +225,24 @@
      */
     public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas,
             boolean forceOverride) throws AppSearchException {
-        SchemaProto schemaProto = getSchemaProto();
-
-        SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
-
-        SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
-        for (AppSearchSchema schema : schemas) {
-            SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
-            newSchemaBuilder.addTypes(schemaTypeProto);
-        }
-
-        // Combine the existing schema (which may have types from other databases) with this
-        // database's new schema. Modifies the existingSchemaBuilder.
-        Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder,
-                newSchemaBuilder.build());
-
-        SetSchemaResultProto setSchemaResultProto;
         mReadWriteLock.writeLock().lock();
         try {
+            SchemaProto.Builder existingSchemaBuilder = getSchemaProto().toBuilder();
+
+            SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
+            for (AppSearchSchema schema : schemas) {
+                SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
+                newSchemaBuilder.addTypes(schemaTypeProto);
+            }
+
+            // Combine the existing schema (which may have types from other databases) with this
+            // database's new schema. Modifies the existingSchemaBuilder.
+            RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(databaseName,
+                    existingSchemaBuilder,
+                    newSchemaBuilder.build());
+
             // Apply schema
-            setSchemaResultProto =
+            SetSchemaResultProto setSchemaResultProto =
                     mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
 
             // Determine whether it succeeded.
@@ -231,7 +264,9 @@
             }
 
             // Update derived data structures.
-            mSchemaMap.put(databaseName, newTypeNames);
+            mSchemaMap.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
+            mVisibilityStore.updateSchemas(databaseName,
+                    rewrittenSchemaResults.mDeletedQualifiedTypes);
 
             // Determine whether to schedule an immediate optimize.
             if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
@@ -248,6 +283,40 @@
     }
 
     /**
+     * Update the visibility settings for this app.
+     *
+     * <p>This method belongs to the mutate group
+     *
+     * @param databaseName                      The name of the database where the
+     *                                          visibility settings will apply.
+     * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform
+     *                                          surfaces
+     * @throws AppSearchException on IcingSearchEngine error
+     */
+    public void setVisibility(@NonNull String databaseName,
+            @NonNull Set<String> schemasHiddenFromPlatformSurfaces)
+            throws AppSearchException {
+        mReadWriteLock.writeLock().lock();
+        try {
+            String databasePrefix = getDatabasePrefix(databaseName);
+            Set<String> qualifiedSchemasHiddenFromPlatformSurface =
+                    new ArraySet<>(schemasHiddenFromPlatformSurfaces.size());
+            for (String schema : schemasHiddenFromPlatformSurfaces) {
+                Set<String> existingSchemas = mSchemaMap.get(databaseName);
+                if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) {
+                    throw new AppSearchException(AppSearchResult.RESULT_NOT_FOUND,
+                            "Unknown schema(s): " + schemasHiddenFromPlatformSurfaces
+                                    + " provided during setVisibility.");
+                }
+                qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema);
+            }
+            mVisibilityStore.setVisibility(databaseName, qualifiedSchemasHiddenFromPlatformSurface);
+        } finally {
+            mReadWriteLock.writeLock().lock();
+        }
+    }
+
+    /**
      * Adds a document to the AppSearch index.
      *
      * <p>This method belongs to mutate group.
@@ -341,6 +410,9 @@
     public SearchResultPage globalQuery(
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec) throws AppSearchException {
+        // TODO(b/169883602): Check if the platform is querying us at a higher level. At this
+        //  point, we should add all platform-surfaceable schemas assuming the querier has been
+        //  verified.
         return doQuery(mNamespaceMap.keySet(), queryExpression, searchSpec);
     }
 
@@ -475,8 +547,7 @@
      *
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    @VisibleForTesting
-    public void reset() throws AppSearchException {
+    private void reset() throws AppSearchException {
         ResetResultProto resetResultProto;
         mReadWriteLock.writeLock().lock();
         try {
@@ -484,12 +555,27 @@
             mOptimizeIntervalCount = 0;
             mSchemaMap.clear();
             mNamespaceMap.clear();
+
+            // Must be called after everything else since VisibilityStore may repopulate
+            // IcingSearchEngine with an initial schema.
+            mVisibilityStore.handleReset();
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
         checkSuccess(resetResultProto.getStatus());
     }
 
+    /** Wrapper around schema changes */
+    @VisibleForTesting
+    static class RewrittenSchemaResults {
+        // Any database-qualified types that used to exist in the schema, but are deleted in the
+        // new one.
+        final Set<String> mDeletedQualifiedTypes = new ArraySet<>();
+
+        // Database-qualified types that were part of the new schema.
+        final Set<String> mRewrittenQualifiedTypes = new ArraySet<>();
+    }
+
     /**
      * Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
      * Rewritten types will be added to the {@code existingSchema}.
@@ -499,10 +585,11 @@
      *                       instances. Will be mutated to contain the properly rewritten schema
      *                       types from {@code newSchema}.
      * @param newSchema      Schema with types to add to the {@code existingSchema}.
-     * @return a Set contains all remaining qualified schema type names in given database.
+     * @return a RewrittenSchemaResults contains all qualified schema type names in the given
+     * database as well as a set of schema types that were deleted from the database.
      */
     @VisibleForTesting
-    Set<String> rewriteSchema(@NonNull String databaseName,
+    RewrittenSchemaResults rewriteSchema(@NonNull String databaseName,
             @NonNull SchemaProto.Builder existingSchema,
             @NonNull SchemaProto newSchema) throws AppSearchException {
         String prefix = getDatabasePrefix(databaseName);
@@ -533,7 +620,9 @@
             newTypesToProto.put(newSchemaType, typeConfigBuilder.build());
         }
 
-        Set<String> newSchemaTypesName = newTypesToProto.keySet();
+        // newTypesToProto is modified below, so we need a copy first
+        RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
+        rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet());
 
         // Combine the existing schema (which may have types from other databases) with this
         // database's new schema. Modifies the existingSchemaBuilder.
@@ -548,13 +637,14 @@
                 // All types existing before but not in newSchema should be removed.
                 existingSchema.removeTypes(i);
                 --i;
+                rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType);
             }
         }
         // We've been removing existing types from newTypesToProto, so everything that remains is
         // new.
         existingSchema.addAllTypes(newTypesToProto.values());
 
-        return newSchemaTypesName;
+        return rewrittenSchemaResults;
     }
 
     /**
@@ -601,21 +691,11 @@
      * @param documentBuilder The document to mutate
      */
     @VisibleForTesting
-    void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) {
-        int delimiterIndex;
-        if ((delimiterIndex = documentBuilder.getSchema().indexOf(DATABASE_DELIMITER)) != -1) {
-            // Rewrite the type name to remove the prefix.
-            // Add 1 to include the char size of the DATABASE_DELIMITER
-            String newSchema = documentBuilder.getSchema().substring(delimiterIndex + 1);
-            documentBuilder.setSchema(newSchema);
-        }
-
-        if ((delimiterIndex = documentBuilder.getNamespace().indexOf(DATABASE_DELIMITER)) != -1) {
-            // Rewrite the namespace to remove the prefix.
-            // Add 1 to include the char size of the DATABASE_DELIMITER
-            String newNamespace = documentBuilder.getNamespace().substring(delimiterIndex + 1);
-            documentBuilder.setNamespace(newNamespace);
-        }
+    void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
+            throws AppSearchException {
+        // Rewrite the type name and namespace to remove the prefix.
+        documentBuilder.setSchema(removeDatabasePrefix(documentBuilder.getSchema()));
+        documentBuilder.setNamespace(removeDatabasePrefix(documentBuilder.getNamespace()));
 
         // Recurse into derived documents
         for (int propertyIdx = 0;
@@ -711,13 +791,45 @@
         return schemaProto.getSchema();
     }
 
+    /** Returns true if {@code databaseName} has a {@code schemaType} */
+    @GuardedBy("mReadWriteLock")
+    boolean hasSchemaType(@NonNull String databaseName, @NonNull String schemaType) {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(schemaType);
+
+        Set<String> schemaTypes = mSchemaMap.get(databaseName);
+        if (schemaTypes == null) {
+            return false;
+        }
+
+        return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType);
+    }
+
+    /** Returns a set of all databases AppSearchImpl knows about. */
     @NonNull
-    private String getDatabasePrefix(@NonNull String databaseName) {
+    Set<String> getDatabases() {
+        return mSchemaMap.keySet();
+    }
+
+    @NonNull
+    private static String getDatabasePrefix(@NonNull String databaseName) {
         // TODO(b/170370381): Reconsider the way we separate database names for security reasons.
         return databaseName + DATABASE_DELIMITER;
     }
 
     @NonNull
+    private static String removeDatabasePrefix(@NonNull String prefixedString)
+            throws AppSearchException {
+        int delimiterIndex;
+        if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
+            // Add 1 to include the char size of the DATABASE_DELIMITER
+            return prefixedString.substring(delimiterIndex + 1);
+        }
+        throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
+                "The prefixed value doesn't contains a valid database name.");
+    }
+
+    @NonNull
     private String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
         int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
         if (delimiterIndex == -1) {
@@ -805,7 +917,7 @@
 
     /** Remove the rewritten schema types from any result documents. */
     private SearchResultPage rewriteSearchResultProto(
-            @NonNull SearchResultProto searchResultProto) {
+            @NonNull SearchResultProto searchResultProto) throws AppSearchException {
         SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder();
         for (int i = 0; i < searchResultProto.getResultsCount(); i++) {
             if (searchResultProto.getResults(i).hasDocument()) {
@@ -825,6 +937,11 @@
         return mIcingSearchEngine.getOptimizeInfo();
     }
 
+    @VisibleForTesting
+    VisibilityStore getVisibilityStore() {
+        return mVisibilityStore;
+    }
+
     /**
      * Converts an erroneous status code to an AppSearchException. Callers should ensure that
      * the status code is not OK or WARNING_DATA_LOSS.
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
index 47f3a36..6a201f5 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
@@ -21,6 +21,7 @@
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchBatchResult;
 import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.GetByUriRequest;
@@ -29,11 +30,14 @@
 import androidx.appsearch.app.SearchResults;
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.app.SetSchemaRequest;
+import androidx.appsearch.app.SetVisibilityRequest;
 import androidx.appsearch.localstorage.util.FutureUtil;
+import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 
@@ -75,6 +79,29 @@
 
     @Override
     @NonNull
+    public ListenableFuture<AppSearchResult<Void>> setVisibility(
+            @NonNull SetVisibilityRequest request) {
+        Preconditions.checkNotNull(request);
+        return execute(() -> {
+            try {
+                Set<AppSearchSchema> appSearchSchemasHiddenFromPlatformSurfaces =
+                        request.getSchemasHiddenFromPlatformSurfaces();
+                Set<String> schemasHiddenFromPlatformSurfaces =
+                        new ArraySet<>(appSearchSchemasHiddenFromPlatformSurfaces.size());
+                for (AppSearchSchema schema : appSearchSchemasHiddenFromPlatformSurfaces) {
+                    schemasHiddenFromPlatformSurfaces.add(schema.getSchemaTypeName());
+                }
+
+                mAppSearchImpl.setVisibility(mDatabaseName, schemasHiddenFromPlatformSurfaces);
+                return AppSearchResult.newSuccessfulResult(/*value=*/ null);
+            } catch (Throwable t) {
+                return throwableToFailedResult(t);
+            }
+        });
+    }
+
+    @Override
+    @NonNull
     public ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
             @NonNull PutDocumentsRequest request) {
         Preconditions.checkNotNull(request);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
new file mode 100644
index 0000000..c19f37d
--- /dev/null
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
@@ -0,0 +1,250 @@
+/*
+ * 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.appsearch.localstorage;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.exceptions.AppSearchException;
+import androidx.collection.ArrayMap;
+import androidx.collection.ArraySet;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists
+ * the visibility settings and reloads them on initialization.
+ *
+ * <p>The VisibilityStore creates a document for each database. This document holds the visibility
+ * settings that apply to that database. The VisibilityStore also creates a schema for these
+ * documents and has its own database so that its data doesn't interfere with any clients' data.
+ * It persists the document and schema through AppSearchImpl.
+ *
+ * <p>These visibility settings are used to ensure AppSearch queries respect the clients'
+ * settings on who their data is visible to.
+ *
+ * <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
+ * higher level.
+ *
+ * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
+ * this class. Take care to not cause any circular dependencies.
+ */
+class VisibilityStore {
+    // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
+    @VisibleForTesting
+    static final String SCHEMA_TYPE = "Visibility";
+    // Property that holds the list of platform-hidden schemas, as part of the visibility
+    // settings.
+    @VisibleForTesting
+    static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
+    // Database name to prefix all visibility schemas and documents with. Special-cased to
+    // minimize the chance of collision with a client-supplied database.
+    @VisibleForTesting
+    static final String DATABASE_NAME = "$$__AppSearch__Database";
+    // Namespace of documents that contain visibility settings
+    private static final String NAMESPACE = "namespace";
+    private final AppSearchImpl mAppSearchImpl;
+
+    // The map contains schemas that are platform-hidden for each database. All schemas in the map
+    // have a database name prefix.
+    private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>();
+
+    /**
+     * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
+     * before using the object.
+     *
+     * @param appSearchImpl AppSearchImpl instance
+     */
+    VisibilityStore(@NonNull AppSearchImpl appSearchImpl) {
+        mAppSearchImpl = appSearchImpl;
+    }
+
+    /**
+     * Initializes schemas and member variables to track visibility settings.
+     *
+     * <p>This is kept separate from the constructor because this will call methods on
+     * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
+     * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both
+     * AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
+     *
+     * @throws AppSearchException AppSearchException on AppSearchImpl error.
+     */
+    public void initialize() throws AppSearchException {
+        if (!mAppSearchImpl.hasSchemaType(DATABASE_NAME, SCHEMA_TYPE)) {
+            // Schema type doesn't exist yet. Add it.
+            mAppSearchImpl.setSchema(DATABASE_NAME,
+                    Collections.singleton(new AppSearchSchema.Builder(SCHEMA_TYPE)
+                            .addProperty(new AppSearchSchema.PropertyConfig.Builder(
+                                    PLATFORM_HIDDEN_PROPERTY)
+                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                    .setCardinality(
+                                            AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                    .build())
+                            .build()),
+                    /*forceOverride=*/ false);
+        }
+
+        // Populate visibility settings map
+        for (String database : mAppSearchImpl.getDatabases()) {
+            if (database.equals(DATABASE_NAME)) {
+                // Our own database. Skip
+                continue;
+            }
+
+            try {
+                // Note: We use the other clients' database names as uris
+                GenericDocument document = mAppSearchImpl.getDocument(
+                        DATABASE_NAME, NAMESPACE, /*uri=*/ database);
+
+                String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
+                mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
+            } catch (AppSearchException e) {
+                if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
+                    // TODO(b/172068212): This indicates some desync error. We were expecting a
+                    //  document, but didn't find one. Should probably reset AppSearch instead of
+                    //  ignoring it.
+                    continue;
+                }
+                // Otherwise, this is some other error we should pass up.
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Update visibility settings for the {@code databaseName}.
+     *
+     * @param schemasToRemove Database-prefixed schemas that should be removed
+     */
+    public void updateSchemas(@NonNull String databaseName,
+            @NonNull Set<String> schemasToRemove) throws AppSearchException {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(schemasToRemove);
+
+        GenericDocument visibilityDocument;
+        try {
+            visibilityDocument = mAppSearchImpl.getDocument(
+                    DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
+        } catch (AppSearchException e) {
+            if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
+                // This might be the first time we're seeing visibility changes for a database.
+                // Create a new visibility document.
+                mAppSearchImpl.putDocument(DATABASE_NAME, new GenericDocument.Builder(
+                        /*uri=*/ databaseName, SCHEMA_TYPE)
+                        .setNamespace(NAMESPACE).build());
+
+                // Since we know there was nothing that existed before, we don't need to remove
+                // anything either. Return early.
+                return;
+            }
+            // Otherwise, this is some real error we should pass up.
+            throw e;
+        }
+
+        String[] hiddenSchemas =
+                visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
+        if (hiddenSchemas == null) {
+            // Nothing to remove.
+            return;
+        }
+
+        // Create a new set so we can remove from it.
+        Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas));
+        boolean changed = remainingSchemas.removeAll(schemasToRemove);
+        if (!changed) {
+            // Nothing was actually removed. Can return early.
+            return;
+        }
+
+        // Update our persisted document
+        // TODO(b/171882200): Switch to a .toBuilder API when it's available.
+        GenericDocument.Builder newVisibilityDocument = new GenericDocument.Builder(
+                /*uri=*/ databaseName, SCHEMA_TYPE)
+                .setNamespace(NAMESPACE);
+        if (!remainingSchemas.isEmpty()) {
+            newVisibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
+                    remainingSchemas.toArray(new String[0]));
+        }
+        mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build());
+
+        // Update derived data structures
+        mPlatformHiddenMap.put(databaseName, remainingSchemas);
+    }
+
+    /**
+     * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
+     * overwritten.
+     *
+     * @param databaseName          Database name that owns the {@code platformHiddenSchemas}.
+     * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from
+     *                              the platform.
+     * @throws AppSearchException on AppSearchImpl error.
+     */
+    public void setVisibility(@NonNull String databaseName,
+            @NonNull Set<String> platformHiddenSchemas) throws AppSearchException {
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(platformHiddenSchemas);
+
+        // Persist the document
+        GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(
+                /*uri=*/ databaseName, SCHEMA_TYPE)
+                .setNamespace(NAMESPACE);
+        if (!platformHiddenSchemas.isEmpty()) {
+            visibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
+                    platformHiddenSchemas.toArray(new String[0]));
+        }
+        mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
+
+        // Update derived data structures.
+        mPlatformHiddenMap.put(databaseName, platformHiddenSchemas);
+    }
+
+    /**
+     * Returns the set of database-qualified schemas in {@code databaseName} that are hidden from
+     * the platform.
+     *
+     * @param databaseName Database name to retrieve schemas for
+     * @return Set of database-qualified schemas that are hidden from the platform. Empty set if
+     * none exist.
+     */
+    @NonNull
+    public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) {
+        Preconditions.checkNotNull(databaseName);
+        Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName);
+        if (platformHiddenSchemas == null) {
+            return Collections.emptySet();
+        }
+        return platformHiddenSchemas;
+    }
+
+    /**
+     * Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a
+     * first-initialized state.
+     *
+     * @throws AppSearchException on AppSearchImpl error.
+     */
+    public void handleReset() throws AppSearchException {
+        mPlatformHiddenMap.clear();
+        initialize();
+    }
+}
diff --git a/benchmark/benchmark-macro-runtime/build.gradle b/benchmark/benchmark-macro-runtime/build.gradle
index 1bd0b20..afef1e9 100644
--- a/benchmark/benchmark-macro-runtime/build.gradle
+++ b/benchmark/benchmark-macro-runtime/build.gradle
@@ -78,3 +78,11 @@
     inceptionYear = "2020"
     description = "Android Benchmark - Macrobenchmark Runtime"
 }
+
+// Define a task dependency so the app is installed before we run macro benchmarks.
+tasks.getByPath(':benchmark:benchmark-macro-runtime:connectedCheck')
+        .dependsOn(
+                tasks.getByPath(
+                        ':benchmark:integration-tests:benchmark-simple-macro-benchmark-target:installRelease'
+                )
+        )
diff --git a/benchmark/benchmark-macro-runtime/src/androidTest/AndroidManifest.xml b/benchmark/benchmark-macro-runtime/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..03b4e49
--- /dev/null
+++ b/benchmark/benchmark-macro-runtime/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.benchmark.macro.runtime.test">
+     <!--
+       The Macro Benchmark Sample needs to launch activities in
+       `androidx.benchmark.integration.macro.target` APK.
+
+        The Macro Benchmark Library uses `PackageManager` to query for activities. This requires
+        the test APK to declare that `androidx.benchmark.integration.macro.target` be visible to
+        the APK (given Android 11's package visibility rules).
+     -->
+    <queries>
+        <package android:name="androidx.benchmark.integration.macro.target" />
+    </queries>
+</manifest>
diff --git a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt
new file mode 100644
index 0000000..f58c8ca
--- /dev/null
+++ b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.macro.test
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.benchmark.macro.compile
+import androidx.benchmark.macro.device
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import junit.framework.TestCase.assertTrue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.fail
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class ActionsTest {
+    @Test
+    @Ignore("Figure out why we can't launch the default activity.")
+    fun killTest() {
+        val scope = MacrobenchmarkScope(PACKAGE_NAME)
+        scope.pressHome()
+        scope.launchPackageAndWait { intent ->
+            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+        }
+        assertTrue(isProcessAlive(PACKAGE_NAME))
+        scope.killProcess()
+        assertFalse(isProcessAlive(PACKAGE_NAME))
+    }
+
+    @Test
+    @Ignore("Compilation modes are a bit flaky")
+    fun compile_speedProfile() {
+        val scope = MacrobenchmarkScope(PACKAGE_NAME)
+        val iterations = 1
+        var executions = 0
+        val compilation = CompilationMode.SpeedProfile(warmupIterations = iterations)
+        compilation.compile(PACKAGE_NAME) {
+            executions += 1
+            scope.pressHome()
+            scope.launchPackageAndWait { intent ->
+                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+            }
+        }
+        assertEquals(iterations, executions)
+    }
+
+    @Test
+    @Ignore("Compilation modes are a bit flaky")
+    fun compile_speed() {
+        val compilation = CompilationMode.Speed
+        compilation.compile(PACKAGE_NAME) {
+            fail("Should never be called for $compilation")
+        }
+    }
+
+    private fun processes(): List<String> {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        val output = instrumentation.device().executeShellCommand("ps -A")
+        return output.split("\r?\n".toRegex())
+    }
+
+    private fun isProcessAlive(packageName: String): Boolean {
+        return processes().any { it.contains(packageName) }
+    }
+
+    companion object {
+        private const val PACKAGE_NAME = "androidx.benchmark.integration.macro.target"
+    }
+}
diff --git a/benchmark/benchmark-macro-runtime/src/main/AndroidManifest.xml b/benchmark/benchmark-macro-runtime/src/main/AndroidManifest.xml
index e156320..e956539 100644
--- a/benchmark/benchmark-macro-runtime/src/main/AndroidManifest.xml
+++ b/benchmark/benchmark-macro-runtime/src/main/AndroidManifest.xml
@@ -13,5 +13,5 @@
   ~ 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.benchmark.macro.runtime"/>
+-->
+<manifest package="androidx.benchmark.macro.runtime" />
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Actions.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Actions.kt
index eb7e5a5..25e42b1 100644
--- a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Actions.kt
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Actions.kt
@@ -76,15 +76,14 @@
         val response = device.executeShellCommand("killall -s SIGUSR1 $packageName")
         if (response.isNotBlank()) {
             Log.d(TAG, "Received dump profile response $response")
-        } else {
-            throw RuntimeException("Failed to dump profile for $packageName")
+            throw RuntimeException("Failed to dump profile for $packageName ($response)")
         }
         delay(profileSaveTimeout)
     }
     val response = device.executeShellCommand("cmd package compile -f -m $mode $packageName")
     if (!response.contains("Success")) {
         Log.d(TAG, "Received compile cmd response: $response")
-        throw RuntimeException("Failed to compile $packageName")
+        throw RuntimeException("Failed to compile $packageName ($response)")
     }
 }
 
@@ -135,6 +134,6 @@
     instrumentation.device().executeShellCommand("setenforce $policy")
 }
 
-private fun Instrumentation.device(): UiDevice {
+internal fun Instrumentation.device(): UiDevice {
     return UiDevice.getInstance(this)
 }
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 7578e57..2b1ac70 100644
--- a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -34,8 +34,7 @@
     private val device = UiDevice.getInstance(instrumentation)
 
     fun launchPackageAndWait(block: (Intent) -> Unit) {
-        val intent = context.packageManager
-            .getLaunchIntentForPackage(packageName)!!
+        val intent = context.packageManager.getLaunchIntentForPackage(packageName)!!
         block(intent)
         context.startActivity(intent)
         device.wait(
@@ -103,7 +102,7 @@
     }
 }
 
-private fun CompilationMode.compile(packageName: String, block: () -> Unit) {
+internal fun CompilationMode.compile(packageName: String, block: () -> Unit) {
     if (this == CompilationMode.None) {
         return // nothing to do
     }
diff --git a/benchmark/benchmark-simple-macro-benchmark/build.gradle b/benchmark/benchmark-simple-macro-benchmark/build.gradle
new file mode 100644
index 0000000..7cb8d16
--- /dev/null
+++ b/benchmark/benchmark-simple-macro-benchmark/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 28
+    }
+}
+
+dependencies {
+    androidTestImplementation(project(":benchmark:benchmark-macro-runtime"))
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+}
+
+// Define a task dependency so the app is installed before we run macro benchmarks.
+tasks.getByPath(':benchmark:benchmark-simple-macro-benchmark:connectedCheck')
+    .dependsOn(
+            tasks.getByPath(
+                    ':benchmark:integration-tests:benchmark-simple-macro-benchmark-target:installRelease'
+            )
+    )
diff --git a/benchmark/benchmark-simple-macro-benchmark/src/androidTest/AndroidManifest.xml b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..69d043c
--- /dev/null
+++ b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.benchmark.macro.sample">
+
+    <!--
+    The Macro Benchmark Sample needs to launch activities in
+    `androidx.benchmark.integration.macro.target` APK.
+
+    The Macro Benchmark Library uses `PackageManager` to query for activities. This requires
+     the test APK to declare that `androidx.benchmark.integration.macro.target` be visible to
+     the APK (given Android 11's package visibility rules).
+    -->
+    <queries>
+        <package android:name="androidx.benchmark.integration.macro.target" />
+    </queries>
+</manifest>
diff --git a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
similarity index 82%
rename from benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt
rename to benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
index ad64f07..4a2f637 100644
--- a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt
+++ b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.benchmark.macro
+package androidx.benchmark.macro.sample
 
 import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.CpuUsageMetric
+import androidx.benchmark.macro.StartupTimingMetric
+import androidx.benchmark.macro.macrobenchmark
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import org.junit.Ignore
@@ -31,7 +35,7 @@
     @Ignore("Not running the test in CI")
     fun basicTest() = macrobenchmark(
         "benchmarkUniqueName",
-        packageName = "com.android.settings",
+        packageName = "androidx.benchmark.integration.macro.target",
         listOf(StartupTimingMetric(), CpuUsageMetric()),
         CompilationMode.Speed,
         killProcessEachIteration = true,
diff --git a/benchmark/benchmark-simple-macro-benchmark/src/main/AndroidManifest.xml b/benchmark/benchmark-simple-macro-benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..005f65e
--- /dev/null
+++ b/benchmark/benchmark-simple-macro-benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest package="androidx.benchmark.macro.sample" />
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index b0d1aa1..98a9387 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -22,8 +22,17 @@
     id("androidx.benchmark")
 }
 
+android {
+    defaultConfig {
+        // 18 needed for UI automator dependency, via benchmark-perfetto
+        minSdkVersion 18
+    }
+}
+
 dependencies {
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
+    androidTestImplementation(project(":benchmark:benchmark-perfetto"))
+    androidTestImplementation(project(":tracing:tracing-ktx"))
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/benchmark/benchmark/src/androidTest/AndroidManifest.xml b/benchmark/benchmark/src/androidTest/AndroidManifest.xml
index b20d8c1..b8419c3 100644
--- a/benchmark/benchmark/src/androidTest/AndroidManifest.xml
+++ b/benchmark/benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
new file mode 100644
index 0000000..f3f224c
--- /dev/null
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.benchmark
+
+import android.os.Trace
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.benchmark.perfetto.PerfettoRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.tracing.trace
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class PerfettoOverheadBenchmark {
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    @get:Rule
+    val perfettoRule = PerfettoRule()
+
+    /**
+     * Empty baseline, no tracing. Expect similar results to [TrivialJavaBenchmark.nothing].
+     */
+    @Test
+    fun empty() = benchmarkRule.measureRepeated {}
+
+    /**
+     * The trace section within runWithTimingDisabled, even though not measured, can impact the
+     * results of a small benchmark significantly.
+     */
+    @Test
+    fun runWithTimingDisabled() = benchmarkRule.measureRepeated {
+        runWithTimingDisabled { /* nothing*/ }
+    }
+
+    /**
+     * Trace section adds ~5us (depending on many factors) in this ideal case, but will be
+     * significantly worse in a real benchmark, as there's more computation to interfere with.
+     */
+    @Test
+    fun traceBeginEnd() = benchmarkRule.measureRepeated {
+        Trace.beginSection("foo")
+        Trace.endSection()
+    }
+
+    /**
+     * Dupe of [traceBeginEnd], just using [trace].
+     */
+    @Test
+    fun traceBlock() = benchmarkRule.measureRepeated {
+        trace("foo") { /* nothing */ }
+    }
+}
diff --git a/benchmark/integration-tests/macro-benchmark-target/README.md b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/README.md
similarity index 100%
rename from benchmark/integration-tests/macro-benchmark-target/README.md
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/README.md
diff --git a/benchmark/integration-tests/macro-benchmark-target/build.gradle b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/build.gradle
similarity index 100%
rename from benchmark/integration-tests/macro-benchmark-target/build.gradle
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/build.gradle
diff --git a/benchmark/integration-tests/macro-benchmark-target/src/main/AndroidManifest.xml b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/AndroidManifest.xml
similarity index 75%
rename from benchmark/integration-tests/macro-benchmark-target/src/main/AndroidManifest.xml
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/AndroidManifest.xml
index 30bd4e4..726a4df 100644
--- a/benchmark/integration-tests/macro-benchmark-target/src/main/AndroidManifest.xml
+++ b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<!--
   ~ Copyright 2020 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,22 +14,25 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.benchmark.integration.macro">
+    package="androidx.benchmark.integration.macro.target">
 
     <application
         android:allowBackup="false"
         android:supportsRtl="true"
         android:theme="@style/Theme.AppCompat">
 
-        <activity android:name=".MainActivity">
+        <!--
+        The activity needs to be exported so the Macro Benchmark Sample can discover activities
+        under the new package visibility changes for Android 11.
+         -->
+        <activity
+            android:exported="true"
+            android:name=".MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.LAUNCHER" />
-                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
 
     </application>
-
 </manifest>
diff --git a/benchmark/integration-tests/macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/MainActivity.kt b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/target/MainActivity.kt
similarity index 94%
rename from benchmark/integration-tests/macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/MainActivity.kt
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/target/MainActivity.kt
index 780f79c..60477d9 100644
--- a/benchmark/integration-tests/macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/MainActivity.kt
+++ b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/java/androidx/benchmark/integration/macro/target/MainActivity.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.benchmark.integration.macro
+package androidx.benchmark.integration.macro.target
 
 import android.os.Bundle
 import android.widget.TextView
diff --git a/benchmark/integration-tests/macro-benchmark-target/src/main/res/layout/activity_main.xml b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/res/layout/activity_main.xml
similarity index 100%
rename from benchmark/integration-tests/macro-benchmark-target/src/main/res/layout/activity_main.xml
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/res/layout/activity_main.xml
diff --git a/benchmark/integration-tests/macro-benchmark-target/src/main/res/values/strings.xml b/benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/res/values/strings.xml
similarity index 100%
rename from benchmark/integration-tests/macro-benchmark-target/src/main/res/values/strings.xml
rename to benchmark/integration-tests/benchmark-simple-macro-benchmark-target/src/main/res/values/strings.xml
diff --git a/benchmark/macro/src/main/res/raw/trace_config.textproto b/benchmark/macro/src/main/res/raw/trace_config.textproto
deleted file mode 100644
index d2d3500..0000000
--- a/benchmark/macro/src/main/res/raw/trace_config.textproto
+++ /dev/null
@@ -1,119 +0,0 @@
-# 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.
-
-# proto-message: TraceConfig
-
-# Enable periodic flushing of the trace buffer into the output file.
-write_into_file: true
-
-# Writes the userspace buffer into the file every 1s.
-file_write_period_ms: 1000
-
-# See b/126487238 - we need to guarantee ordering of events.
-flush_period_ms: 30000
-
-# The trace buffers needs to be big enough to hold |file_write_period_ms| of
-# trace data. The trace buffer sizing depends on the number of trace categories
-# enabled and the device activity.
-
-# RSS events
-buffers {
-  size_kb: 16384
-  fill_policy: RING_BUFFER
-}
-
-# procfs polling
-buffers {
-  size_kb: 8192
-  fill_policy: RING_BUFFER
-}
-
-data_sources {
-  config {
-    name: "linux.ftrace"
-    target_buffer: 0
-    ftrace_config {
-      # These parameters affect only the kernel trace buffer size and how
-      # frequently it gets moved into the userspace buffer defined above.
-      buffer_size_kb: 16384
-      drain_period_ms: 250
-
-      # We need to do process tracking to ensure kernel ftrace events targeted at short-lived
-      # threads are associated correctly
-      ftrace_events: "task/task_newtask"
-      ftrace_events: "task/task_rename"
-      ftrace_events: "sched/sched_process_exit"
-      ftrace_events: "sched/sched_process_free"
-
-      # Memory events
-      ftrace_events: "rss_stat"
-      ftrace_events: "ion_heap_shrink"
-      ftrace_events: "ion_heap_grow"
-      ftrace_events: "ion/ion_stat"
-      ftrace_events: "oom_score_adj_update"
-
-      # Old (kernel) LMK
-      ftrace_events: "lowmemorykiller/lowmemory_kill"
-
-      # New (userspace) LMK
-      atrace_apps: "lmkd"
-      # Added for userspace annotation in the platform scenario test app
-      atrace_apps: "android.platform.test.scenario"
-
-      atrace_categories: "am"
-      atrace_categories: "dalvik"
-      atrace_categories: "binder_driver"
-    }
-  }
-}
-
-data_sources {
-  config {
-    name: "linux.process_stats"
-    target_buffer: 1
-    process_stats_config {
-      proc_stats_poll_ms: 10000
-    }
-  }
-}
-
-data_sources {
-  config {
-    name: "linux.sys_stats"
-    target_buffer: 1
-    sys_stats_config {
-      meminfo_period_ms: 1000
-      meminfo_counters: MEMINFO_MEM_TOTAL
-      meminfo_counters: MEMINFO_MEM_FREE
-      meminfo_counters: MEMINFO_MEM_AVAILABLE
-      meminfo_counters: MEMINFO_BUFFERS
-      meminfo_counters: MEMINFO_CACHED
-      meminfo_counters: MEMINFO_SWAP_CACHED
-      meminfo_counters: MEMINFO_ACTIVE
-      meminfo_counters: MEMINFO_INACTIVE
-      meminfo_counters: MEMINFO_ACTIVE_ANON
-      meminfo_counters: MEMINFO_INACTIVE_ANON
-      meminfo_counters: MEMINFO_ACTIVE_FILE
-      meminfo_counters: MEMINFO_INACTIVE_FILE
-      meminfo_counters: MEMINFO_UNEVICTABLE
-      meminfo_counters: MEMINFO_SWAP_TOTAL
-      meminfo_counters: MEMINFO_SWAP_FREE
-      meminfo_counters: MEMINFO_DIRTY
-      meminfo_counters: MEMINFO_WRITEBACK
-      meminfo_counters: MEMINFO_ANON_PAGES
-      meminfo_counters: MEMINFO_MAPPED
-      meminfo_counters: MEMINFO_SHMEM
-    }
-  }
-}
\ No newline at end of file
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
index eb69b8c..e562cb8 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
@@ -91,6 +91,7 @@
         block()
         val dst = destinationPath(traceName)
         stop(dst.absolutePath)
+        Log.d(PerfettoRule.TAG, "Finished recording to ${dst.absolutePath}")
         reportAdditionalFileToCopy("perfetto_trace", dst.absolutePath)
     } finally {
         cancel()
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index ceb8bb8..e9f75e7 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -84,27 +84,9 @@
 class AndroidXPlugin : Plugin<Project> {
     override fun apply(project: Project) {
         if (project.isRoot) throw Exception("Root project should use AndroidXRootPlugin instead")
-        // This has to be first due to bad behavior by DiffAndDocs which is triggered on the root
-        // project. It calls evaluationDependsOn on each subproject. This eagerly causes evaluation
-        // *during* the root build.gradle evaluation. The subproject then applies this plugin (while
-        // we're still halfway through applying it on the root). The check licenses code runs on the
-        // subproject which then looks for the root project task to add itself as a dependency of.
-        // Without the root project having created the task prior to DiffAndDocs running this fails.
-        // TODO(alanv): do not use evaluationDependsOn in DiffAndDocs to break this cycle!
-        project.configureExternalDependencyLicenseCheck()
-
         val extension = project.extensions.create<AndroidXExtension>(EXTENSION_NAME, project)
-
-        // This has to be first due to bad behavior by DiffAndDocs. It fails if this configuration
-        // is called after DiffAndDocs.configureDiffAndDocs. b/129762955
-        project.configureMavenArtifactUpload(extension)
-
-        if (project.isCoverageEnabled()) {
-            project.configureJacoco()
-        }
-
         // Perform different actions based on which plugins have been applied to the project.
-        // Many of the actions overlap, ex. API tracking and documentation.
+        // Many of the actions overlap, ex. API tracking.
         project.plugins.all { plugin ->
             when (plugin) {
                 is JavaPlugin -> configureWithJavaPlugin(project, extension)
@@ -118,13 +100,17 @@
 
         // Configure all Jar-packing tasks for hermetic builds.
         project.tasks.withType(Jar::class.java).configureEach { it.configureForHermeticBuild() }
-
         project.tasks.withType(Copy::class.java).configureEach { it.configureForHermeticBuild() }
 
         // copy host side test results to DIST
         project.tasks.withType(Test::class.java) { task -> configureTestTask(project, task) }
 
         project.configureTaskTimeouts()
+        project.configureMavenArtifactUpload(extension)
+        project.configureExternalDependencyLicenseCheck()
+        if (project.isCoverageEnabled()) {
+            project.configureJacoco()
+        }
     }
 
     /**
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
index fe0eb44..741a228 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
@@ -16,8 +16,11 @@
 
 package androidx.build
 
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.AppPlugin
 import com.android.build.gradle.LibraryExtension
 import com.android.build.gradle.LibraryPlugin
+import com.android.build.gradle.TestedExtension
 import org.gradle.api.DomainObjectCollection
 import org.gradle.api.Plugin
 import org.gradle.api.Project
@@ -40,28 +43,13 @@
                     val library = project.extensions.findByType(LibraryExtension::class.java)
                         ?: throw Exception("Failed to find Android extension")
 
-                    library.defaultConfig.minSdkVersion(21)
+                    project.configureAndroidCommonOptions(library)
+                }
+                is AppPlugin -> {
+                    val app = project.extensions.findByType(AppExtension::class.java)
+                        ?: throw Exception("Failed to find Android extension")
 
-                    // TODO(148540713): remove this exclusion when Lint can support using multiple lint jars
-                    project.configurations.getByName("lintChecks").exclude(
-                        mapOf("module" to "lint-checks")
-                    )
-                    // TODO: figure out how to apply this to multiplatform modules
-                    project.dependencies.add(
-                        "lintChecks",
-                        project.dependencies.project(
-                            mapOf(
-                                "path" to ":compose:internal-lint-checks",
-                                "configuration" to "shadow"
-                            )
-                        )
-                    )
-
-                    library.lintOptions.apply {
-                        // Too many Kotlin features require synthetic accessors - we want to rely on R8 to
-                        // remove these accessors
-                        disable("SyntheticAccessor")
-                    }
+                    project.configureAndroidCommonOptions(app)
                 }
                 is KotlinBasePluginWrapper -> {
                     val conf = project.configurations.create("kotlinPlugin")
@@ -113,6 +101,31 @@
             }
         }
 
+        private fun Project.configureAndroidCommonOptions(testedExtension: TestedExtension) {
+            testedExtension.defaultConfig.minSdkVersion(21)
+
+            testedExtension.lintOptions.apply {
+                // Too many Kotlin features require synthetic accessors - we want to rely on R8 to
+                // remove these accessors
+                disable("SyntheticAccessor")
+            }
+
+            // TODO(148540713): remove this exclusion when Lint can support using multiple lint jars
+            configurations.getByName("lintChecks").exclude(
+                mapOf("module" to "lint-checks")
+            )
+            // TODO: figure out how to apply this to multiplatform modules
+            dependencies.add(
+                "lintChecks",
+                project.dependencies.project(
+                    mapOf(
+                        "path" to ":compose:internal-lint-checks",
+                        "configuration" to "shadow"
+                    )
+                )
+            )
+        }
+
         private fun Project.configureManifests() {
             val libraryExtension = project.extensions.findByType<LibraryExtension>() ?: return
             libraryExtension.apply {
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index ddd2351..3ac2ad9 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -88,6 +88,7 @@
     val SQLITE = LibraryGroup("androidx.sqlite", LibraryVersions.SQLITE)
     val SWIPEREFRESHLAYOUT = LibraryGroup("androidx.swiperefreshlayout", LibraryVersions.SWIPEREFRESHLAYOUT)
     val TESTSCREENSHOT = LibraryGroup("androidx.test.screenshot", LibraryVersions.TESTSCREENSHOT)
+    val TEXT = LibraryGroup("androidx.text", LibraryVersions.TEXT)
     val TEXTCLASSIFIER = LibraryGroup("androidx.textclassifier", LibraryVersions.TEXTCLASSIFIER)
     val TRACING = LibraryGroup("androidx.tracing", LibraryVersions.TRACING)
     val TRANSITION = LibraryGroup("androidx.transition", LibraryVersions.TRANSITION)
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index ca8e239..2bb6f18 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -33,7 +33,7 @@
     val AUTOFILL = Version("1.1.0-rc01")
     val BENCHMARK = Version("1.1.0-alpha02")
     val BIOMETRIC = Version("1.1.0-rc01")
-    val BROWSER = Version("1.3.0-beta01")
+    val BROWSER = Version("1.3.0-rc01")
     val BUILDSRC_TESTS = Version("1.0.0-alpha01")
     val CAMERA = Version("1.0.0-beta12")
     val CAMERA_EXTENSIONS = Version("1.0.0-alpha19")
@@ -84,7 +84,7 @@
     val NAVIGATION = Version("2.4.0-alpha01")
     val NAVIGATION_COMPOSE = Version("1.0.0-alpha02")
     val PAGING = Version("3.0.0-alpha09")
-    val PAGING_COMPOSE = Version("1.0.0-alpha01")
+    val PAGING_COMPOSE = Version("1.0.0-alpha02")
     val PALETTE = Version("1.1.0-alpha01")
     val PRINT = Version("1.1.0-beta01")
     val PERCENTLAYOUT = Version("1.1.0-alpha01")
@@ -110,6 +110,7 @@
     val SQLITE_INSPECTOR = Version("2.1.0-alpha01")
     val SWIPEREFRESHLAYOUT = Version("1.2.0-alpha01")
     val TESTSCREENSHOT = Version("1.0.0-alpha01")
+    val TEXT = Version("1.0.0-alpha01")
     val TEXTCLASSIFIER = Version("1.0.0-alpha03")
     val TRACING = Version("1.0.0")
     val TRANSITION = Version("1.4.0-rc01")
diff --git a/busytown/androidx-multiplatform.sh b/busytown/androidx-multiplatform.sh
new file mode 120000
index 0000000..71f42d2
--- /dev/null
+++ b/busytown/androidx-multiplatform.sh
@@ -0,0 +1 @@
+androidx_multiplatform.sh
\ No newline at end of file
diff --git a/busytown/androidx-multiplatform.sh b/busytown/androidx_multiplatform.sh
similarity index 100%
rename from busytown/androidx-multiplatform.sh
rename to busytown/androidx_multiplatform.sh
diff --git a/camera/camera-camera2/api/public_plus_experimental_1.0.0-beta12.txt b/camera/camera-camera2/api/public_plus_experimental_1.0.0-beta12.txt
index 9777f11..f20c9b9 100644
--- a/camera/camera-camera2/api/public_plus_experimental_1.0.0-beta12.txt
+++ b/camera/camera-camera2/api/public_plus_experimental_1.0.0-beta12.txt
@@ -9,6 +9,13 @@
 
 package androidx.camera.camera2.interop {
 
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> addCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+    method public static androidx.camera.camera2.interop.Camera2CameraControl from(androidx.camera.core.CameraControl);
+    method public androidx.camera.camera2.interop.CaptureRequestOptions getCaptureRequestOptions();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+  }
+
   @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraFilter {
     method public static androidx.camera.core.CameraFilter createCameraFilter(androidx.camera.camera2.interop.Camera2CameraFilter.Camera2Filter);
   }
@@ -34,6 +41,20 @@
     method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
   }
 
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public class CaptureRequestOptions implements androidx.camera.core.impl.ReadableConfig {
+    ctor public CaptureRequestOptions(androidx.camera.core.impl.Config);
+    method public <ValueT> ValueT? getCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public androidx.camera.core.impl.Config getConfig();
+  }
+
+  public static final class CaptureRequestOptions.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.camera2.interop.CaptureRequestOptions> {
+    ctor public CaptureRequestOptions.Builder();
+    method public androidx.camera.camera2.interop.CaptureRequestOptions build();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder clearCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+  }
+
   @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
   }
 
diff --git a/camera/camera-camera2/api/public_plus_experimental_current.txt b/camera/camera-camera2/api/public_plus_experimental_current.txt
index 9777f11..f20c9b9 100644
--- a/camera/camera-camera2/api/public_plus_experimental_current.txt
+++ b/camera/camera-camera2/api/public_plus_experimental_current.txt
@@ -9,6 +9,13 @@
 
 package androidx.camera.camera2.interop {
 
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> addCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+    method public static androidx.camera.camera2.interop.Camera2CameraControl from(androidx.camera.core.CameraControl);
+    method public androidx.camera.camera2.interop.CaptureRequestOptions getCaptureRequestOptions();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+  }
+
   @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraFilter {
     method public static androidx.camera.core.CameraFilter createCameraFilter(androidx.camera.camera2.interop.Camera2CameraFilter.Camera2Filter);
   }
@@ -34,6 +41,20 @@
     method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
   }
 
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public class CaptureRequestOptions implements androidx.camera.core.impl.ReadableConfig {
+    ctor public CaptureRequestOptions(androidx.camera.core.impl.Config);
+    method public <ValueT> ValueT? getCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public androidx.camera.core.impl.Config getConfig();
+  }
+
+  public static final class CaptureRequestOptions.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.camera2.interop.CaptureRequestOptions> {
+    ctor public CaptureRequestOptions.Builder();
+    method public androidx.camera.camera2.interop.CaptureRequestOptions build();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder clearCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+  }
+
   @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
   }
 
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index dff082b..33822d5 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -30,7 +30,7 @@
 
     implementation("androidx.core:core:1.1.0")
     api("androidx.annotation:annotation:1.0.0")
-    api("androidx.annotation:annotation-experimental:1.0.0-beta01")
+    api("androidx.annotation:annotation-experimental:1.0.0-rc01")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation(GUAVA_LISTENABLE_FUTURE)
     implementation(AUTO_VALUE_ANNOTATIONS)
@@ -44,6 +44,7 @@
     testImplementation(ROBOLECTRIC)
     testImplementation(MOCKITO_CORE)
     testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(project(":annotation:annotation-experimental"))
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation(project(":camera:camera-testing"))
 
@@ -60,6 +61,7 @@
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    androidTestImplementation(project(":annotation:annotation-experimental"))
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation("org.jetbrains.kotlinx:atomicfu:0.13.1")
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraXInitTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraXInitTest.kt
new file mode 100644
index 0000000..e35c679
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraXInitTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraX
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.GrantPermissionRule
+import com.google.common.truth.Truth
+import com.google.common.util.concurrent.ListenableFuture
+import org.junit.AfterClass
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class CameraXInitTest {
+
+    // Please don't use CameraUtil.grantCameraPermissionAndPreTest. This test verifies the CameraX
+    // initialization can successfully done on real device.
+    @get:Rule
+    val permissionRule: GrantPermissionRule =
+        GrantPermissionRule.grant(android.Manifest.permission.CAMERA)
+
+    companion object {
+        private lateinit var cameraXListenableFuture: ListenableFuture<CameraX>
+        private val context = ApplicationProvider.getApplicationContext<Context>()
+        private val pm = context.packageManager
+
+        @BeforeClass
+        @JvmStatic
+        fun classSetup() {
+            cameraXListenableFuture = CameraX.getOrCreateInstance(context)
+        }
+
+        @AfterClass
+        @JvmStatic
+        fun tearDown() {
+            CameraX.shutdown().get()
+        }
+    }
+
+    @Before
+    fun setup() {
+        // Only test the device when it have least one camera. Please don't use the
+        // CameraUtil.deviceHasCamera() to check the camera, it might ignore the test if the
+        // camera device in bad status.
+        assumeTrue(
+            pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) || pm.hasSystemFeature(
+                PackageManager.FEATURE_CAMERA_FRONT
+            )
+        )
+    }
+
+    @Test
+    fun initOnDevice() {
+        cameraXListenableFuture.get()
+
+        Truth.assertThat(CameraX.isInitialized()).isTrue()
+    }
+
+    @Test
+    fun initOnDevice_hasCamera() {
+        val cameraX = cameraXListenableFuture.get()
+
+        try {
+            if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+                CameraSelector.DEFAULT_BACK_CAMERA.select(cameraX.cameraRepository.cameras)
+            }
+            if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
+                CameraSelector.DEFAULT_FRONT_CAMERA.select(cameraX.cameraRepository.cameras)
+            }
+        } catch (e: IllegalArgumentException) {
+            // Wrap the exception with specific error message for dashboard bug collection.
+            throw java.lang.IllegalArgumentException("CameraIdList_incorrect:" + Build.MODEL, e)
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java
new file mode 100644
index 0000000..65f54a8
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2CameraControlDeviceTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.interop;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Range;
+
+import androidx.annotation.experimental.UseExperimental;
+import androidx.camera.camera2.Camera2Config;
+import androidx.camera.camera2.internal.Camera2CameraControlImpl;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.CameraX;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.core.internal.CameraUseCaseAdapter;
+import androidx.camera.testing.CameraUtil;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.GrantPermissionRule;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+public final class Camera2CameraControlDeviceTest {
+    private static final Range<Integer> FAKE_RANGE = new Range<>(0, 30);
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private CameraSelector mCameraSelector;
+    private CameraCaptureSession.CaptureCallback mMockCaptureCallback =
+            mock(CameraCaptureSession.CaptureCallback.class);
+    private Context mContext;
+    private CameraUseCaseAdapter mCamera;
+    private Camera2CameraControl mCamera2CameraControl;
+    private Camera2CameraControlImpl mCamera2CameraControlImpl;
+
+    @Rule
+    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
+            Manifest.permission.CAMERA);
+
+    @Before
+    public void setUp() {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK));
+        mContext = ApplicationProvider.getApplicationContext();
+        CameraX.initialize(mContext, Camera2Config.defaultConfig());
+        mCameraSelector = new CameraSelector.Builder().requireLensFacing(
+                CameraSelector.LENS_FACING_BACK).build();
+        mCamera = CameraUtil.createCameraUseCaseAdapter(mContext, mCameraSelector);
+        mCamera2CameraControlImpl = (Camera2CameraControlImpl) mCamera.getCameraControl();
+        mCamera2CameraControl = mCamera2CameraControlImpl.getCamera2CameraControl();
+        mMockCaptureCallback = mock(CameraCaptureSession.CaptureCallback.class);
+    }
+
+    @After
+    public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void canGetInteropApi() {
+        assertThat(Camera2CameraControl.from(mCamera2CameraControlImpl))
+                .isSameInstanceAs(mCamera2CameraControl);
+    }
+
+    @Test
+    public void canSetAndRetrieveCaptureRequestOptions() {
+        bindUseCase();
+        CaptureRequestOptions.Builder builder = new CaptureRequestOptions.Builder()
+                .setCaptureRequestOption(
+                        CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE)
+                .setCaptureRequestOption(
+                        CaptureRequest.COLOR_CORRECTION_MODE,
+                        CameraMetadata.COLOR_CORRECTION_MODE_FAST);
+        mCamera2CameraControl.setCaptureRequestOptions(builder.build());
+
+        assertThat(mCamera2CameraControl.getCaptureRequestOptions().getCaptureRequestOption(
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, null)).isEqualTo(FAKE_RANGE);
+        assertThat(mCamera2CameraControl.getCaptureRequestOptions().getCaptureRequestOption(
+                CaptureRequest.COLOR_CORRECTION_MODE, null)).isEqualTo(
+                CameraMetadata.COLOR_CORRECTION_MODE_FAST);
+    }
+
+    @Test
+    public void canSubmitCaptureRequestOptions_beforeBinding() {
+        ListenableFuture<Void> future = updateCamera2Option(
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE);
+        bindUseCase();
+
+        assertFutureCompletes(future);
+
+        verifyCaptureRequestParameter(mMockCaptureCallback,
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE);
+    }
+
+    @Test
+    public void canSubmitCaptureRequestOptions_afterBinding() {
+        bindUseCase();
+        ListenableFuture<Void> future = updateCamera2Option(
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE);
+
+        assertFutureCompletes(future);
+
+        verifyCaptureRequestParameter(mMockCaptureCallback,
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE);
+    }
+
+    @Test
+    public void canClearCaptureRequestOptions() {
+        bindUseCase();
+        CaptureRequestOptions.Builder builder = new CaptureRequestOptions.Builder()
+                .setCaptureRequestOption(
+                        CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, FAKE_RANGE)
+                .setCaptureRequestOption(CaptureRequest.COLOR_CORRECTION_MODE,
+                        CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF);
+
+        ListenableFuture<Void> future =
+                mCamera2CameraControl.setCaptureRequestOptions(builder.build());
+
+        assertFutureCompletes(future);
+
+        builder.clearCaptureRequestOption(CaptureRequest.COLOR_CORRECTION_MODE);
+
+        future = mCamera2CameraControl.setCaptureRequestOptions(builder.build());
+
+        assertFutureCompletes(future);
+
+        assertThat(mCamera2CameraControl.getCaptureRequestOptions().getCaptureRequestOption(
+                CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, null)).isEqualTo(FAKE_RANGE);
+        assertThat(mCamera2CameraControl.getCaptureRequestOptions().getCaptureRequestOption(
+                CaptureRequest.COLOR_CORRECTION_MODE, null)).isEqualTo(null);
+
+        ArgumentCaptor<CaptureRequest> captureRequest =
+                ArgumentCaptor.forClass(CaptureRequest.class);
+        verify(mMockCaptureCallback, timeout(5000).atLeastOnce()).onCaptureCompleted(
+                any(CameraCaptureSession.class),
+                captureRequest.capture(), any(TotalCaptureResult.class));
+        CaptureRequest request = captureRequest.getValue();
+        assertThat(request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE)).isEqualTo(FAKE_RANGE);
+        assertThat(request.get(CaptureRequest.COLOR_CORRECTION_MODE)).isNotEqualTo(
+                CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF);
+    }
+
+    @Test
+    public void canOverrideAfMode() {
+        updateCamera2Option(CaptureRequest.CONTROL_AF_MODE,
+                CaptureRequest.CONTROL_AF_MODE_OFF);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AF_MODE,
+                CaptureRequest.CONTROL_AF_MODE_OFF);
+    }
+
+    @Test
+    public void canOverrideAeMode() {
+        updateCamera2Option(CaptureRequest.CONTROL_AE_MODE,
+                CaptureRequest.CONTROL_AE_MODE_OFF);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AE_MODE,
+                CaptureRequest.CONTROL_AE_MODE_OFF);
+    }
+
+    @Test
+    public void canOverrideAwbMode() {
+        updateCamera2Option(CaptureRequest.CONTROL_AWB_MODE,
+                CaptureRequest.CONTROL_AWB_MODE_OFF);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AWB_MODE,
+                CaptureRequest.CONTROL_AWB_MODE_OFF);
+    }
+
+    @Test
+    public void canOverrideScalarCropRegion() throws Exception {
+        // scalar crop region must be larger than the region defined
+        // by SCALER_AVAILABLE_MAX_DIGITAL_ZOOM otherwise it could cause a crash on some devices.
+        // Thus we cannot simply specify some random crop region.
+        Rect cropRegion = getZoom2XCropRegion();
+        updateCamera2Option(CaptureRequest.SCALER_CROP_REGION, cropRegion);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.SCALER_CROP_REGION,
+                cropRegion);
+    }
+
+    @Test
+    public void canOverrideAfRegion() {
+        MeteringRectangle[] meteringRectangles = new MeteringRectangle[]{
+                new MeteringRectangle(0, 0, 100, 100, MeteringRectangle.METERING_WEIGHT_MAX)
+        };
+        updateCamera2Option(CaptureRequest.CONTROL_AF_REGIONS, meteringRectangles);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AF_REGIONS,
+                meteringRectangles);
+    }
+
+    @Test
+    public void canOverrideAeRegion() {
+        MeteringRectangle[] meteringRectangles = new MeteringRectangle[]{
+                new MeteringRectangle(0, 0, 100, 100, MeteringRectangle.METERING_WEIGHT_MAX)
+        };
+        updateCamera2Option(CaptureRequest.CONTROL_AE_REGIONS, meteringRectangles);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AE_REGIONS,
+                meteringRectangles);
+    }
+
+    @Test
+    public void canOverrideAwbRegion() {
+        MeteringRectangle[] meteringRectangles = new MeteringRectangle[]{
+                new MeteringRectangle(0, 0, 100, 100, MeteringRectangle.METERING_WEIGHT_MAX)
+        };
+        updateCamera2Option(CaptureRequest.CONTROL_AWB_REGIONS, meteringRectangles);
+        bindUseCase();
+
+        verifyCaptureRequestParameter(mMockCaptureCallback, CaptureRequest.CONTROL_AWB_REGIONS,
+                meteringRectangles);
+    }
+
+    private Rect getZoom2XCropRegion() throws Exception {
+        AtomicReference<String> cameraIdRef = new AtomicReference<>();
+        String cameraId = ((CameraInfoInternal) mCamera.getCameraInfo()).getCameraId();
+        cameraIdRef.set(cameraId);
+
+        CameraManager cameraManager =
+                (CameraManager) mInstrumentation.getContext().getSystemService(
+                        Context.CAMERA_SERVICE);
+        CameraCharacteristics characteristics =
+                cameraManager.getCameraCharacteristics(cameraIdRef.get());
+        assumeTrue(
+                characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)
+                        >= 2);
+        Rect sensorRect = characteristics
+                .get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+        return new Rect(sensorRect.centerX() - sensorRect.width() / 4,
+                sensorRect.centerY() - sensorRect.height() / 4,
+                sensorRect.centerX() + sensorRect.width() / 4,
+                sensorRect.centerY() + sensorRect.height() / 4);
+    }
+
+    private void bindUseCase() {
+        ImageAnalysis.Builder imageAnalysisBuilder = new ImageAnalysis.Builder();
+        new Camera2Interop.Extender<>(imageAnalysisBuilder).setSessionCaptureCallback(
+                mMockCaptureCallback);
+        ImageAnalysis imageAnalysis = imageAnalysisBuilder.build();
+        // set analyzer to make it active.
+        imageAnalysis.setAnalyzer(CameraXExecutors.highPriorityExecutor(),
+                mock(ImageAnalysis.Analyzer.class));
+
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+        mCamera2CameraControl = Camera2CameraControl.from(mCamera.getCameraControl());
+    }
+
+    private <T> ListenableFuture<Void> updateCamera2Option(CaptureRequest.Key<T> key, T value) {
+        CaptureRequestOptions bundle = new CaptureRequestOptions.Builder()
+                .setCaptureRequestOption(key, value)
+                .build();
+        return mCamera2CameraControl.setCaptureRequestOptions(bundle);
+    }
+
+    private <T> void verifyCaptureRequestParameter(
+            CameraCaptureSession.CaptureCallback mockCallback,
+            CaptureRequest.Key<T> key,
+            T value) {
+        ArgumentCaptor<CaptureRequest> captureRequest =
+                ArgumentCaptor.forClass(CaptureRequest.class);
+        verify(mockCallback, timeout(5000).atLeastOnce()).onCaptureCompleted(
+                any(CameraCaptureSession.class),
+                captureRequest.capture(), any(TotalCaptureResult.class));
+        CaptureRequest request = captureRequest.getValue();
+        assertThat(request.get(key)).isEqualTo(value);
+    }
+
+    private <T> T assertFutureCompletes(ListenableFuture<T> future) {
+        T result = null;
+        try {
+            result = future.get(5, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            fail("future fail:" + e);
+        }
+        return result;
+    }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2ImplConfig.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2ImplConfig.java
index 9f234d5..b0debf9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2ImplConfig.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2ImplConfig.java
@@ -24,20 +24,20 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.experimental.UseExperimental;
+import androidx.camera.camera2.interop.CaptureRequestOptions;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.ExtendableBuilder;
 import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.MutableConfig;
 import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.OptionsBundle;
-import androidx.camera.core.impl.ReadableConfig;
-
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * Internal shared implementation details for camera 2 interop.
  */
-public final class Camera2ImplConfig implements ReadableConfig {
+@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+public final class Camera2ImplConfig extends CaptureRequestOptions {
 
     /** @hide */
     @RestrictTo(Scope.LIBRARY)
@@ -71,22 +71,26 @@
     @RestrictTo(Scope.LIBRARY)
     public static final Option<CameraEventCallbacks> CAMERA_EVENT_CALLBACK_OPTION =
             Option.create("camera2.cameraEvent.callback", CameraEventCallbacks.class);
+
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY)
+    public static final Option<Object> CAPTURE_REQUEST_TAG_OPTION = Option.create(
+            "camera2.captureRequest.tag", Object.class);
+
     // *********************************************************************************************
 
-    private final Config mConfig;
-
     /**
-     * Creates a Camera2InteropInternal for reading Camera2 options from the given config.
+     * Creates a Camera2ImplConfig for reading Camera2 options from the given config.
      *
      * @param config The config that potentially contains Camera2 options.
      */
     public Camera2ImplConfig(@NonNull Config config) {
-        mConfig = config;
+        super(config);
     }
 
     // Unfortunately, we can't get the Class<T> from the CaptureRequest.Key, so we're forced to
     // erase the type. This shouldn't be a problem as long as we are only using these options
-    // within the Camera2InteropInternal and Camera2InteropInternal.Builder classes.
+    // within the Camera2ImplConfig and Camera2ImplConfig.Builder classes.
 
     /** @hide */
     @RestrictTo(Scope.LIBRARY)
@@ -96,44 +100,14 @@
     }
 
     /**
-     * Returns a value for the given {@link CaptureRequest.Key}.
-     *
-     * @param key            The key to retrieve.
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @param <ValueT>       The type of the value.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Nullable
-    public <ValueT> ValueT getCaptureRequestOption(
-            @NonNull CaptureRequest.Key<ValueT> key, @Nullable ValueT valueIfMissing) {
-        @SuppressWarnings(
-                "unchecked") // Type should have been only set via Builder#setCaptureRequestOption()
-                Option<ValueT> opt = (Option<ValueT>) Camera2ImplConfig.createCaptureRequestOption(
-                key);
-        return mConfig.retrieveOption(opt, valueIfMissing);
-    }
-
-
-    /**
      * Returns all capture request options contained in this configuration.
      *
      * @hide
      */
     @RestrictTo(Scope.LIBRARY)
     @NonNull
-    public Set<Option<?>> getCaptureRequestOptions() {
-        final Set<Option<?>> optionSet = new HashSet<>();
-        findOptions(
-                Camera2ImplConfig.CAPTURE_REQUEST_ID_STEM,
-                new OptionMatcher() {
-                    @Override
-                    public boolean onOptionMatched(@NonNull Option<?> option) {
-                        optionSet.add(option);
-                        return true;
-                    }
-                });
-        return optionSet;
+    public CaptureRequestOptions getCaptureRequestOptions() {
+        return CaptureRequestOptions.Builder.from(getConfig()).build();
     }
 
     /**
@@ -147,7 +121,7 @@
      * configuration.
      */
     public int getCaptureRequestTemplate(int valueIfMissing) {
-        return mConfig.retrieveOption(TEMPLATE_TYPE_OPTION, valueIfMissing);
+        return getConfig().retrieveOption(TEMPLATE_TYPE_OPTION, valueIfMissing);
     }
 
     /**
@@ -160,7 +134,7 @@
     @Nullable
     public CameraDevice.StateCallback getDeviceStateCallback(
             @Nullable CameraDevice.StateCallback valueIfMissing) {
-        return mConfig.retrieveOption(DEVICE_STATE_CALLBACK_OPTION, valueIfMissing);
+        return getConfig().retrieveOption(DEVICE_STATE_CALLBACK_OPTION, valueIfMissing);
     }
 
 
@@ -174,7 +148,7 @@
     @Nullable
     public CameraCaptureSession.StateCallback getSessionStateCallback(
             @Nullable CameraCaptureSession.StateCallback valueIfMissing) {
-        return mConfig.retrieveOption(SESSION_STATE_CALLBACK_OPTION, valueIfMissing);
+        return getConfig().retrieveOption(SESSION_STATE_CALLBACK_OPTION, valueIfMissing);
     }
 
     /**
@@ -187,7 +161,7 @@
     @Nullable
     public CameraCaptureSession.CaptureCallback getSessionCaptureCallback(
             @Nullable CameraCaptureSession.CaptureCallback valueIfMissing) {
-        return mConfig.retrieveOption(SESSION_CAPTURE_CALLBACK_OPTION, valueIfMissing);
+        return getConfig().retrieveOption(SESSION_CAPTURE_CALLBACK_OPTION, valueIfMissing);
     }
 
     /**
@@ -200,13 +174,19 @@
     @Nullable
     public CameraEventCallbacks getCameraEventCallback(
             @Nullable CameraEventCallbacks valueIfMissing) {
-        return mConfig.retrieveOption(CAMERA_EVENT_CALLBACK_OPTION, valueIfMissing);
+        return getConfig().retrieveOption(CAMERA_EVENT_CALLBACK_OPTION, valueIfMissing);
     }
 
-    @NonNull
-    @Override
-    public Config getConfig() {
-        return mConfig;
+    /**
+     * Returns the capture request tag.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    public Object getCaptureRequestTag(@Nullable Object valueIfMissing) {
+        return getConfig().retrieveOption(CAPTURE_REQUEST_TAG_OPTION, valueIfMissing);
     }
 
     /**
@@ -244,11 +224,10 @@
          * {@link OptionPriority}.
          */
         @NonNull
-        public <ValueT> Camera2ImplConfig.Builder setCaptureRequestOptionWithPriority(
+        public <ValueT> Builder setCaptureRequestOptionWithPriority(
                 @NonNull CaptureRequest.Key<ValueT> key, @NonNull ValueT value,
                 @NonNull OptionPriority priority) {
-            Option<Object> opt =
-                    Camera2ImplConfig.createCaptureRequestOption(key);
+            Option<Object> opt = Camera2ImplConfig.createCaptureRequestOption(key);
             mMutableOptionsBundle.insertOption(opt, priority, value);
             return this;
         }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index 81a2ccf..bb39cdb 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -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.
@@ -34,11 +34,15 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.annotation.CameraExecutor;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.internal.compat.workaround.AeFpsRange;
 import androidx.camera.camera2.internal.compat.workaround.AutoFlashAEModeDisabler;
+import androidx.camera.camera2.interop.Camera2CameraControl;
+import androidx.camera.camera2.interop.CaptureRequestOptions;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.FocusMeteringResult;
@@ -53,6 +57,7 @@
 import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.annotation.ExecutedBy;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.util.Preconditions;
@@ -97,7 +102,8 @@
  * requests end in {@code ImmediateFailedFuture}. Any cached requests are dropped.</li>
  * </ul>
  */
-final class Camera2CameraControlImpl implements CameraControlInternal {
+@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+public class Camera2CameraControlImpl implements CameraControlInternal {
     private static final String TAG = "Camera2CameraControlImp";
     @VisibleForTesting
     final CameraControlSessionCallback mSessionCallback;
@@ -107,6 +113,7 @@
     private final Object mLock = new Object();
     private final CameraCharacteristicsCompat mCameraCharacteristics;
     private final ControlUpdateCallback mControlUpdateCallback;
+
     private final SessionConfig.Builder mSessionConfigBuilder = new SessionConfig.Builder();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     volatile Rational mPreviewAspectRatio = null;
@@ -114,6 +121,7 @@
     private final ZoomControl mZoomControl;
     private final TorchControl mTorchControl;
     private final ExposureControl mExposureControl;
+    private final Camera2CameraControl mCamera2CameraControl;
     private final AeFpsRange mAeFpsRange;
     @GuardedBy("mLock")
     private int mUseCount = 0;
@@ -172,9 +180,12 @@
         mZoomControl = new ZoomControl(this, mCameraCharacteristics, mExecutor);
         mTorchControl = new TorchControl(this, mCameraCharacteristics, mExecutor);
         mAeFpsRange = new AeFpsRange(cameraQuirks);
+        mCamera2CameraControl = new Camera2CameraControl(this, mExecutor);
+        mExecutor.execute(
+                () -> addCaptureResultListener(mCamera2CameraControl.getCaptureRequestListener()));
 
         // Initialize the session config
-        mExecutor.execute(this::updateSessionConfig);
+        updateSessionConfig();
     }
 
     /** Increments the use count of the control. */
@@ -228,6 +239,32 @@
         return mExposureControl;
     }
 
+    @NonNull
+    public Camera2CameraControl getCamera2CameraControl() {
+        return mCamera2CameraControl;
+    }
+
+    @Override
+    public void addInteropConfig(@NonNull Config config) {
+        ListenableFuture<Void> future = mCamera2CameraControl.addCaptureRequestOptions(
+                CaptureRequestOptions.Builder.from(config).build());
+        future.addListener(() -> {
+        }, CameraXExecutors.directExecutor());
+    }
+
+    @Override
+    public void clearInteropConfig() {
+        ListenableFuture<Void> future = mCamera2CameraControl.clearCaptureRequestOptions();
+        future.addListener(() -> {
+        }, CameraXExecutors.directExecutor());
+    }
+
+    @NonNull
+    @Override
+    public Config getInteropConfig() {
+        return mCamera2CameraControl.getCamera2ImplConfig();
+    }
+
     /**
      * Set current active state. Set active if it is ready to trigger camera control operation.
      *
@@ -240,6 +277,7 @@
         mZoomControl.setActive(isActive);
         mTorchControl.setActive(isActive);
         mExposureControl.setActive(isActive);
+        mCamera2CameraControl.setActive(isActive);
     }
 
     @ExecutedBy("mExecutor")
@@ -313,7 +351,7 @@
         // update mFlashMode immediately so that following getFlashMode() returns correct value.
         mFlashMode = flashMode;
 
-        mExecutor.execute(this::updateSessionConfig);
+        updateSessionConfig();
     }
 
     /** {@inheritDoc} */
@@ -413,9 +451,20 @@
         return getUseCount() > 0;
     }
 
+    /**
+     * Triggers an update to the session.
+     */
+    public void updateSessionConfig() {
+        mExecutor.execute(this::updateSessionConfigSynchronous);
+    }
+
     @ExecutedBy("mExecutor")
-    void updateSessionConfig() {
+    void updateSessionConfigSynchronous() {
         mSessionConfigBuilder.setImplementationOptions(getSessionOptions());
+        Object tag = mCamera2CameraControl.getCamera2ImplConfig().getCaptureRequestTag(null);
+        if (tag != null && tag instanceof Integer) {
+            mSessionConfigBuilder.addTag(Camera2CameraControl.TAG_KEY, (Integer) tag);
+        }
         mControlUpdateCallback.onCameraControlUpdateSessionConfig(mSessionConfigBuilder.build());
     }
 
@@ -476,7 +525,7 @@
             submitCaptureRequestsInternal(
                     Collections.singletonList(singleRequestBuilder.build()));
         }
-        updateSessionConfig();
+        updateSessionConfigSynchronous();
     }
 
 
@@ -531,6 +580,15 @@
 
         mExposureControl.setCaptureRequestOption(builder);
 
+        Config currentConfig = mCamera2CameraControl.getCamera2ImplConfig();
+        for (Config.Option<?> option : currentConfig.listOptions()) {
+            @SuppressWarnings("unchecked")
+            Config.Option<Object> objectOpt = (Config.Option<Object>) option;
+            builder.getMutableConfig().insertOption(objectOpt,
+                    Config.OptionPriority.ALWAYS_OVERRIDE,
+                    currentConfig.retrieveOption(objectOpt));
+        }
+
         return builder.build();
     }
 
@@ -652,7 +710,7 @@
     }
 
     /** An interface to listen to camera capture results. */
-    interface CaptureResultListener {
+    public interface CaptureResultListener {
         /**
          * Callback to handle camera capture results.
          *
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpacker.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpacker.java
index 6cb7110..99611c7 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpacker.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpacker.java
@@ -16,13 +16,12 @@
 
 package androidx.camera.camera2.internal;
 
-import android.hardware.camera2.CaptureRequest;
-
 import androidx.annotation.NonNull;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
-import androidx.camera.core.impl.Config.Option;
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.UseCaseConfig;
 
@@ -34,6 +33,7 @@
 
     static final Camera2CaptureOptionUnpacker INSTANCE = new Camera2CaptureOptionUnpacker();
 
+    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
     @Override
     public void unpack(@NonNull UseCaseConfig<?> config,
             @NonNull final CaptureConfig.Builder builder) {
@@ -66,17 +66,6 @@
                                 Camera2CaptureCallbacks.createNoOpCallback())));
 
         // Copy extension keys
-        Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder();
-        for (Option<?> option : camera2Config.getCaptureRequestOptions()) {
-            @SuppressWarnings("unchecked")
-            // No way to get actual type info here, so treat as Object
-                    Option<Object> typeErasedOption = (Option<Object>) option;
-            @SuppressWarnings("unchecked")
-            CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
-            configBuilder.setCaptureRequestOptionWithPriority(key,
-                    camera2Config.retrieveOption(typeErasedOption),
-                    camera2Config.getOptionPriority(typeErasedOption));
-        }
-        builder.addImplementationOptions(configBuilder.build());
+        builder.addImplementationOptions(camera2Config.getCaptureRequestOptions());
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
index b900185..74b2b20 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
@@ -23,7 +23,9 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.annotation.experimental.UseExperimental;
+import androidx.camera.camera2.interop.CaptureRequestOptions;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.Logger;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
@@ -67,16 +69,11 @@
         return surfaceList;
     }
 
+    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
     private static void applyImplementationOptionToCaptureBuilder(
             CaptureRequest.Builder builder, Config config) {
-        Camera2ImplConfig camera2Config = new Camera2ImplConfig(config);
-        for (Config.Option<?> option : camera2Config.getCaptureRequestOptions()) {
-            /* Although type is erased below, it is safe to pass it to CaptureRequest.Builder
-            because these option are created via Camera2Interop.Extender.setCaptureRequestOption
-            (CaptureRequest.Key<ValueT> key, ValueT value) and hence the type compatibility of key
-            and value are ensured by the compiler. */
-            @SuppressWarnings("unchecked")
-            Config.Option<Object> typeErasedOption = (Config.Option<Object>) option;
+        CaptureRequestOptions bundle = CaptureRequestOptions.Builder.from(config).build();
+        for (Config.Option<?> option : bundle.listOptions()) {
             @SuppressWarnings("unchecked")
             CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
 
@@ -84,7 +81,7 @@
             //  send back out to the developer
             try {
                 // Ignores keys that don't exist
-                builder.set(key, camera2Config.retrieveOption(typeErasedOption));
+                builder.set(key, bundle.retrieveOption(option));
             } catch (IllegalArgumentException e) {
                 Logger.e(TAG, "CaptureRequest.Key is not supported: " + key);
             }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpacker.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpacker.java
index f6dfcaf..577ad8c 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpacker.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpacker.java
@@ -16,13 +16,12 @@
 
 package androidx.camera.camera2.internal;
 
-import android.hardware.camera2.CaptureRequest;
-
 import androidx.annotation.NonNull;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.impl.Config;
-import androidx.camera.core.impl.Config.Option;
 import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
@@ -36,6 +35,7 @@
 
     static final Camera2SessionOptionUnpacker INSTANCE = new Camera2SessionOptionUnpacker();
 
+    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
     @Override
     public void unpack(@NonNull UseCaseConfig<?> config,
             @NonNull final SessionConfig.Builder builder) {
@@ -82,17 +82,6 @@
         builder.addImplementationOptions(cameraEventConfig);
 
         // Copy extension keys
-        Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder();
-        for (Option<?> option : camera2Config.getCaptureRequestOptions()) {
-            @SuppressWarnings("unchecked")
-            // No way to get actual type info here, so treat as Object
-                    Option<Object> typeErasedOption = (Option<Object>) option;
-            @SuppressWarnings("unchecked")
-            CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
-            configBuilder.setCaptureRequestOptionWithPriority(key,
-                    camera2Config.retrieveOption(typeErasedOption),
-                    camera2Config.getOptionPriority(option));
-        }
-        builder.addImplementationOptions(configBuilder.build());
+        builder.addImplementationOptions(camera2Config.getCaptureRequestOptions());
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index af766a6..5885d6b 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -27,10 +27,12 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.Logger;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CaptureConfig;
@@ -257,6 +259,7 @@
         }
     }
 
+    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
     @NonNull
     private ListenableFuture<Void> openCaptureSession(@NonNull List<Surface> configuredSurfaces,
             @NonNull SessionConfig sessionConfig, @NonNull CameraDevice cameraDevice) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
index a3b15bd..9b8e776 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
@@ -206,7 +206,7 @@
                         mRunningCompleter = completer;
 
                         mCameraControl.addCaptureResultListener(mRunningCaptureResultListener);
-                        mCameraControl.updateSessionConfig();
+                        mCameraControl.updateSessionConfigSynchronous();
                     });
 
                     return "setExposureCompensationIndex[" + exposure + "]";
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
index 1f9eee9..6e915ae 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
@@ -606,13 +606,13 @@
             mIsInAfAutoMode = true;
             mIsAutoFocusCompleted = false;
             mIsFocusSuccessful = false;
-            mCameraControl.updateSessionConfig();
+            mCameraControl.updateSessionConfigSynchronous();
             triggerAf(null);
         } else {
             mIsInAfAutoMode = false;
             mIsAutoFocusCompleted = true; // Don't need to wait for auto-focus
             mIsFocusSuccessful = false;  // False because AF is not triggered.
-            mCameraControl.updateSessionConfig();
+            mCameraControl.updateSessionConfigSynchronous();
         }
 
         mCurrentAfState = CaptureResult.CONTROL_AF_STATE_INACTIVE;
@@ -747,6 +747,6 @@
         mAwbRects = new MeteringRectangle[]{};
 
         mIsInAfAutoMode = false;
-        mCameraControl.updateSessionConfig();
+        mCameraControl.updateSessionConfigSynchronous();
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZoomControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZoomControl.java
index 3526b19..7d79583 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZoomControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZoomControl.java
@@ -147,7 +147,7 @@
             updateLiveData(zoomState);
 
             mZoomImpl.resetZoom();
-            mCamera2CameraControlImpl.updateSessionConfig();
+            mCamera2CameraControlImpl.updateSessionConfigSynchronous();
         }
     }
 
@@ -220,7 +220,7 @@
         updateLiveData(zoomState);
 
         mZoomImpl.setZoomRatio(zoomState.getZoomRatio(), completer);
-        mCamera2CameraControlImpl.updateSessionConfig();
+        mCamera2CameraControlImpl.updateSessionConfigSynchronous();
     }
 
     /**
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java
new file mode 100644
index 0000000..b1aae32b
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.interop;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.internal.Camera2CameraControlImpl;
+import androidx.camera.camera2.internal.annotation.CameraExecutor;
+import androidx.camera.core.CameraControl;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.annotation.ExecutedBy;
+import androidx.camera.core.impl.utils.futures.Futures;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An class that provides ability to interoperate with the {@link android.hardware.camera2} APIs.
+ *
+ * <p>Camera2 specific controls, like capture request options, can be applied through this class.
+ * A Camera2CameraControl can be created from a general {@link CameraControl} which is associated
+ * to a camera. Then the controls will affect all use cases that are using that camera.
+ *
+ * <p>If any option applied by Camera2CameraControl conflicts with the options required by
+ * CameraX internally. The options from Camera2CameraControl will override, which may result in
+ * unexpected behavior depends on the options being applied.
+ */
+@ExperimentalCamera2Interop
+public final class Camera2CameraControl {
+
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY)
+    public static final String TAG_KEY = "Camera2CameraControl";
+
+    private boolean mIsActive = false;
+    private boolean mPendingUpdate = false;
+    private final Camera2CameraControlImpl mCamera2CameraControlImpl;
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @CameraExecutor
+    final Executor mExecutor;
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Camera2ImplConfig.Builder mBuilder = new Camera2ImplConfig.Builder();
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    CallbackToFutureAdapter.Completer<Void> mCompleter;
+    private final Camera2CameraControlImpl.CaptureResultListener mCaptureResultListener =
+            (captureResult) -> {
+                CallbackToFutureAdapter.Completer<Void> completerToSet = null;
+                if (mCompleter != null) {
+                    Object tag = captureResult.getRequest().getTag();
+                    if (tag instanceof TagBundle) {
+                        Integer tagInteger = ((TagBundle) tag).getTag(TAG_KEY);
+                        if (tagInteger != null && tagInteger.equals(mCompleter.hashCode())) {
+                            completerToSet = mCompleter;
+                            mCompleter = null;
+                        }
+                    }
+                }
+                if (completerToSet != null) {
+                    completerToSet.set(null);
+                }
+                // Return false to keep getting captureResult.
+                return false;
+            };
+
+    /**
+     * Creates a new camera control with Camera2 implementation.
+     *
+     * @param camera2CameraControlImpl the camera control this Camera2CameraControl belongs.
+     * @param executor                 the camera executor used to run camera task.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    public Camera2CameraControl(@NonNull Camera2CameraControlImpl camera2CameraControlImpl,
+            @NonNull @CameraExecutor Executor executor) {
+        mCamera2CameraControlImpl = camera2CameraControlImpl;
+        mExecutor = executor;
+    }
+
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    public Camera2CameraControlImpl.CaptureResultListener getCaptureRequestListener() {
+        return mCaptureResultListener;
+    }
+
+    /**
+     * Gets the {@link Camera2CameraControl} from a {@link CameraControl}.
+     *
+     * <p>The {@link CameraControl} is still usable after a {@link Camera2CameraControl} is
+     * obtained from it. Note that the {@link Camera2CameraControl} has higher priority than the
+     * {@link CameraControl}. For example, if
+     * {@link android.hardware.camera2.CaptureRequest#FLASH_MODE} is set through the
+     * {@link Camera2CameraControl}. All {@link CameraControl} features that required
+     * {@link android.hardware.camera2.CaptureRequest#FLASH_MODE} internally like torch may not
+     * work properly.
+     *
+     * @param cameraControl The {@link CameraControl} to get from.
+     * @return The camera control with Camera2 implementation.
+     * @throws IllegalArgumentException if the camera control does not contain the camera2
+     *                                  information (e.g., if CameraX was not initialized with a
+     *                                  {@link androidx.camera.camera2.Camera2Config}).
+     */
+    @NonNull
+    public static Camera2CameraControl from(@NonNull CameraControl cameraControl) {
+        Preconditions.checkArgument(cameraControl instanceof Camera2CameraControlImpl,
+                "CameraControl doesn't contain Camera2 implementation.");
+        return ((Camera2CameraControlImpl) cameraControl).getCamera2CameraControl();
+    }
+
+    /**
+     * Sets a {@link CaptureRequestOptions} and updates the session with the options it
+     * contains.
+     *
+     * <p>This will first clear all options that have already been set, then apply the new options.
+     *
+     * <p>Any values which are in conflict with values already set by CameraX, such as by
+     * {@link androidx.camera.core.CameraControl}, will overwrite the existing values. The
+     * values will be submitted with every repeating and single capture requests issued by
+     * CameraX, which may result in unexpected behavior depending on the values being applied.
+     *
+     * @param bundle The {@link CaptureRequestOptions} which will be set.
+     * @return a {@link ListenableFuture} which completes when the repeating
+     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
+     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
+     * options are set or camera is closed before the current request completes.
+     * Cancelling the ListenableFuture is a no-op.
+     */
+    @NonNull
+    public ListenableFuture<Void> setCaptureRequestOptions(
+            @NonNull CaptureRequestOptions bundle) {
+        clearCaptureRequestOptionsInternal();
+        addCaptureRequestOptionsInternal(bundle);
+
+        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                updateConfig(completer);
+            });
+            return "setCaptureRequestOptions";
+        }));
+    }
+
+    /**
+     * Adds a {@link CaptureRequestOptions} updates the session with the options it
+     * contains.
+     *
+     * <p>The options will be merged with the existing options. If one option is set with a
+     * different value, it will overwrite the existing value.
+     *
+     * <p>Any values which are in conflict with values already set by CameraX, such as by
+     * {@link androidx.camera.core.CameraControl}, will overwrite the existing values. The
+     * values will be submitted with every repeating and single capture requests issued by
+     * CameraX, which may result in unexpected behavior depends on the values being applied.
+     *
+     * @param bundle The {@link CaptureRequestOptions} which will be set.
+     * @return a {@link ListenableFuture} which completes when the repeating
+     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
+     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
+     * options are set or camera is closed before the current request completes.
+     */
+    @NonNull
+    public ListenableFuture<Void> addCaptureRequestOptions(
+            @NonNull CaptureRequestOptions bundle) {
+        addCaptureRequestOptionsInternal(bundle);
+
+        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                updateConfig(completer);
+            });
+            return "addCaptureRequestOptions";
+        }));
+    }
+
+    /**
+     * Gets all the capture request options that is currently applied by the
+     * {@link Camera2CameraControl}.
+     *
+     * <p>It doesn't include the capture request options applied by
+     * the {@link android.hardware.camera2.CameraDevice} templates or by CameraX.
+     *
+     * @return The {@link CaptureRequestOptions}.
+     */
+    @NonNull
+    public CaptureRequestOptions getCaptureRequestOptions() {
+        synchronized (mLock) {
+            return CaptureRequestOptions.Builder.from(mBuilder.build()).build();
+        }
+    }
+
+    /**
+     * Clears all capture request options.
+     *
+     * @return a {@link ListenableFuture} which completes when the repeating
+     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
+     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
+     * options are set or camera is closed before the current request completes.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    public ListenableFuture<Void> clearCaptureRequestOptions() {
+        clearCaptureRequestOptionsInternal();
+
+        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                updateConfig(completer);
+            });
+            return "clearCaptureRequestOptions";
+        }));
+    }
+
+    /**
+     * Gets the {@link Camera2ImplConfig} that is currently applied by the
+     * {@link Camera2CameraControl}.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    public Camera2ImplConfig getCamera2ImplConfig() {
+        synchronized (mLock) {
+            if (mCompleter != null) {
+                mBuilder.getMutableConfig().insertOption(
+                        Camera2ImplConfig.CAPTURE_REQUEST_TAG_OPTION,
+                        mCompleter.hashCode());
+            }
+            return mBuilder.build();
+        }
+    }
+
+    private void addCaptureRequestOptionsInternal(@NonNull CaptureRequestOptions bundle) {
+        synchronized (mLock) {
+            for (Config.Option<?> option : bundle.listOptions()) {
+                @SuppressWarnings("unchecked")
+                Config.Option<Object> objectOpt = (Config.Option<Object>) option;
+                mBuilder.getMutableConfig().insertOption(objectOpt,
+                        bundle.retrieveOption(objectOpt));
+            }
+        }
+    }
+
+    private void clearCaptureRequestOptionsInternal() {
+        synchronized (mLock) {
+            mBuilder = new Camera2ImplConfig.Builder();
+        }
+    }
+
+    @ExecutedBy("mExecutor")
+    private void updateConfig(CallbackToFutureAdapter.Completer<Void> completer) {
+        mPendingUpdate = true;
+        // Complete the future if CaptureResult shows it's submitted successfully.
+        CallbackToFutureAdapter.Completer<Void> completerToCancel = null;
+        if (mCompleter != null) {
+            completerToCancel = mCompleter;
+        }
+        mCompleter = completer;
+        if (mIsActive) {
+            updateSession();
+        }
+        if (completerToCancel != null) {
+            completerToCancel.setException(new CameraControl.OperationCanceledException(
+                    "Camera2CameraControl was updated with new options."));
+        }
+    }
+
+    @ExecutedBy("mExecutor")
+    private void updateSession() {
+        mCamera2CameraControlImpl.updateSessionConfig();
+        mPendingUpdate = false;
+    }
+
+    /**
+     * Set current active state.
+     *
+     * <p>When the state changes from active to inactive, the Camera2 options will be cleared.
+     * When the state changes from inactive to active, a session update will be issued if there's
+     * Camera2 options set while inactive.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    public void setActive(boolean isActive) {
+        mExecutor.execute(() -> setActiveInternal(isActive));
+    }
+
+    @ExecutedBy("mExecutor")
+    private void setActiveInternal(boolean isActive) {
+        if (mIsActive == isActive) {
+            return;
+        }
+
+        mIsActive = isActive;
+
+        if (mIsActive) {
+            if (mPendingUpdate) {
+                updateSession();
+            }
+        } else {
+            clearCaptureRequestOptionsInternal();
+            if (mCompleter != null) {
+                mCompleter.setException(new CameraControl.OperationCanceledException(
+                        "The camera control has became inactive."));
+                mCompleter = null;
+            }
+        }
+    }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/CaptureRequestOptions.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/CaptureRequestOptions.java
new file mode 100644
index 0000000..35eddcd
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/CaptureRequestOptions.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.interop;
+
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.core.ExtendableBuilder;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.MutableConfig;
+import androidx.camera.core.impl.MutableOptionsBundle;
+import androidx.camera.core.impl.OptionsBundle;
+import androidx.camera.core.impl.ReadableConfig;
+
+/**
+ * A bundle of Camera2 capture request options.
+ */
+@ExperimentalCamera2Interop
+public class CaptureRequestOptions implements ReadableConfig {
+
+    private final Config mConfig;
+
+    /**
+     * Creates a CaptureRequestOptions for reading Camera2 capture request options from the
+     * given config.
+     *
+     * @param config The config that potentially contains Camera2 capture request options.
+     */
+    public CaptureRequestOptions(@NonNull Config config) {
+        mConfig = config;
+    }
+
+    /**
+     * Returns a value for the given {@link CaptureRequest.Key} or null if it hasn't been set.
+     *
+     * @param key            The key to retrieve.
+     * @param <ValueT>       The type of the value.
+     * @return The stored value or null if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    public <ValueT> ValueT getCaptureRequestOption(@NonNull CaptureRequest.Key<ValueT> key) {
+        @SuppressWarnings(
+                "unchecked") // Type should have been only set via Builder#setCaptureRequestOption()
+                Option<ValueT> opt = (Option<ValueT>) Camera2ImplConfig.createCaptureRequestOption(
+                key);
+        return mConfig.retrieveOption(opt, null);
+    }
+
+    /**
+     * Returns a value for the given {@link CaptureRequest.Key}.
+     *
+     * @param key            The key to retrieve.
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @param <ValueT>       The type of the value.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    @Nullable
+    public <ValueT> ValueT getCaptureRequestOption(
+            @NonNull CaptureRequest.Key<ValueT> key, @Nullable ValueT valueIfMissing) {
+        @SuppressWarnings(
+                "unchecked") // Type should have been only set via Builder#setCaptureRequestOption()
+                Option<ValueT> opt = (Option<ValueT>) Camera2ImplConfig.createCaptureRequestOption(
+                key);
+        return mConfig.retrieveOption(opt, valueIfMissing);
+    }
+
+    /**
+     * Returns the {@link Config} object associated with this {@link CaptureRequestOptions}.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    @Override
+    public Config getConfig() {
+        return mConfig;
+    }
+
+    /**
+     * Builder for creating {@link CaptureRequestOptions} instance.
+     */
+    public static final class Builder implements ExtendableBuilder<CaptureRequestOptions> {
+
+        private final MutableOptionsBundle mMutableOptionsBundle = MutableOptionsBundle.create();
+
+        /**
+         * Generates a Builder from another Config object.
+         *
+         * @param config An immutable configuration to pre-populate this builder.
+         * @return The new Builder.
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY)
+        @NonNull
+        public static CaptureRequestOptions.Builder from(@NonNull Config config) {
+            CaptureRequestOptions.Builder bundleBuilder = new CaptureRequestOptions.Builder();
+            config.findOptions(
+                    Camera2ImplConfig.CAPTURE_REQUEST_ID_STEM,
+                    option -> {
+                        // Erase the type of the option. Capture request options should only be
+                        // set via Camera2Interop so that the type of the key and value should
+                        // always match.
+                        @SuppressWarnings("unchecked")
+                        Config.Option<Object> objectOpt = (Config.Option<Object>) option;
+                        bundleBuilder.getMutableConfig().insertOption(objectOpt,
+                                config.getOptionPriority(objectOpt),
+                                config.retrieveOption(objectOpt));
+                        return true;
+                    });
+            return bundleBuilder;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY)
+        @Override
+        @NonNull
+        public MutableConfig getMutableConfig() {
+            return mMutableOptionsBundle;
+        }
+
+        /**
+         * Inserts new capture request option with specific {@link CaptureRequest.Key} setting.
+         */
+        @NonNull
+        public <ValueT> CaptureRequestOptions.Builder setCaptureRequestOption(
+                @NonNull CaptureRequest.Key<ValueT> key, @NonNull ValueT value) {
+            Option<Object> opt = Camera2ImplConfig.createCaptureRequestOption(key);
+            mMutableOptionsBundle.insertOption(opt, value);
+            return this;
+        }
+
+        /**
+         * Removes a capture request option with specific {@link CaptureRequest.Key} setting.
+         */
+        @NonNull
+        public <ValueT> CaptureRequestOptions.Builder clearCaptureRequestOption(
+                @NonNull CaptureRequest.Key<ValueT> key) {
+            Config.Option<Object> opt = Camera2ImplConfig.createCaptureRequestOption(key);
+            mMutableOptionsBundle.removeOption(opt);
+            return this;
+        }
+
+
+        /**
+         * Builds an immutable {@link CaptureRequestOptions} from the current state.
+         *
+         * @return A {@link CaptureRequestOptions} populated with the current state.
+         */
+        @Override
+        @NonNull
+        public CaptureRequestOptions build() {
+            return new CaptureRequestOptions(OptionsBundle.from(mMutableOptionsBundle));
+        }
+    }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/ExperimentalCamera2Interop.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/ExperimentalCamera2Interop.java
index 3ad65ec..f9df834 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/ExperimentalCamera2Interop.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/ExperimentalCamera2Interop.java
@@ -18,9 +18,8 @@
 
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
-import androidx.annotation.experimental.Experimental;
-
 import java.lang.annotation.Retention;
+
 /**
  * Denotes that the annotated method uses the experimental methods which allow direct access to
  * camera2 classes.
@@ -37,7 +36,10 @@
  *
  * <p>These will be changed in future release possibly, hence add @Experimental annotation.
  */
+// TODO(b/170599666): Experimental/UseExperimental is deprecated and has to be replaced with
+//  RequiresOptIn/OptIn.
+@SuppressWarnings("deprecation")
 @Retention(CLASS)
-@Experimental
+@androidx.annotation.experimental.Experimental
 public @interface ExperimentalCamera2Interop {
 }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
index d22f6b9..4d18c5e 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
@@ -568,7 +568,8 @@
                 new FocusMeteringAction.Builder(mPoint1).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
 
-        verify(mCamera2CameraControlImpl, times(1)).updateSessionConfig();
+        verify(mCamera2CameraControlImpl, times(1))
+                .updateSessionConfigSynchronous();
     }
 
     @Test
@@ -1022,10 +1023,10 @@
         Mockito.reset(mCamera2CameraControlImpl);
 
         mFocusMeteringControl.cancelFocusAndMetering();
-        verify(mCamera2CameraControlImpl, times(1)).updateSessionConfig();
+        verify(mCamera2CameraControlImpl, times(1))
+                .updateSessionConfigSynchronous();
     }
 
-
     @Test
     public void cancelFocusAndMetering_triggerCancelAfProperly() {
         // If AF is enabled, cancel operation needs to call cancelAfAeTriggerInternal(true, false)
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraControlTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraControlTest.kt
new file mode 100644
index 0000000..bdf3f1e
--- /dev/null
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraControlTest.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.interop
+
+import android.os.Build
+import androidx.annotation.OptIn
+import androidx.camera.core.impl.CameraControlInternal
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+@OptIn(ExperimentalCamera2Interop::class)
+class Camera2CameraControlTest {
+
+    @Test(expected = IllegalArgumentException::class)
+    fun fromCameraControlThrows_whenNotCamera2Impl() {
+        val wrongCameraControl =
+            CameraControlInternal.DEFAULT_EMPTY_INSTANCE
+        Camera2CameraControl.from(wrongCameraControl)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
index b086b65..3e31138 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
@@ -31,6 +31,8 @@
 import androidx.camera.core.UseCase;
 import androidx.camera.core.ViewPort;
 import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.UseCaseConfigFactory;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager;
@@ -90,6 +92,53 @@
     }
 
     @Test
+    public void attachUseCases_restoreInteropConfig() {
+        // Set an config to CameraControl.
+        Config.Option<Integer> option = Config.Option.create("OPTION_ID", Integer.class);
+        Integer value = 1;
+        MutableOptionsBundle originalConfig = MutableOptionsBundle.create();
+        originalConfig.insertOption(option, value);
+        mFakeCamera.getCameraControlInternal().addInteropConfig(originalConfig);
+        CameraUseCaseAdapter cameraUseCaseAdapter = new CameraUseCaseAdapter(mFakeCameraSet,
+                mFakeCameraDeviceSurfaceManager,
+                mUseCaseConfigFactory);
+
+        // This caches the original config and clears it from CameraControl internally.
+        cameraUseCaseAdapter.detachUseCases();
+
+        // Set a different config.
+        mFakeCamera.getCameraControlInternal().addInteropConfig(MutableOptionsBundle.create());
+
+        // This restores the cached config to CameraControl.
+        cameraUseCaseAdapter.attachUseCases();
+
+        // Check the config in CameraControl has the same value as the original config.
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().containsOption(
+                        option)).isTrue();
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().retrieveOption(
+                        option)).isEqualTo(value);
+    }
+
+    @Test
+    public void detachUseCases_clearInteropConfig() {
+        // Set an config to CameraControl.
+        Config config = MutableOptionsBundle.create();
+        mFakeCamera.getCameraControlInternal().addInteropConfig(config);
+        CameraUseCaseAdapter cameraUseCaseAdapter = new CameraUseCaseAdapter(mFakeCameraSet,
+                mFakeCameraDeviceSurfaceManager,
+                mUseCaseConfigFactory);
+
+        // This caches the original config and clears it from CameraControl internally.
+        cameraUseCaseAdapter.detachUseCases();
+
+        // Check the config in CameraControl is empty.
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().listOptions()).isEmpty();
+    }
+
+    @Test
     public void closeCameraUseCaseAdapter() throws CameraUseCaseAdapter.CameraException {
         CameraUseCaseAdapter cameraUseCaseAdapter = new CameraUseCaseAdapter(mFakeCameraSet,
                 mFakeCameraDeviceSurfaceManager,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 7d18ab7..2ef71ce 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -781,6 +781,15 @@
          * <p>If not set, resolutions with aspect ratio 4:3 will be considered in higher
          * priority.
          *
+         * <p>For the following devices, the aspect ratio will be forced to
+         * {@link AspectRatio#RATIO_16_9} regardless of the config. On these devices, the
+         * camera HAL produces a preview with a 16:9 aspect ratio regardless of the aspect ratio
+         * of the preview surface.
+         * <ul>
+         *     <li>SM-J710MN, Samsung Galaxy J7 (2016)
+         *     <li>SM-T580, Samsung Galaxy Tab A J7 (2016)
+         * </ul>
+         *
          * @param aspectRatio The desired Preview {@link AspectRatio}
          * @return The current Builder.
          */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
index 29a0b6d..4bdd4bc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
@@ -96,6 +96,22 @@
     @NonNull
     Rect getSensorRect();
 
+    /**
+     * Adds the Interop configuration.
+     */
+    void addInteropConfig(@NonNull Config config);
+
+    /**
+     * Clears the Interop configuration set previously.
+     */
+    void clearInteropConfig();
+
+    /**
+     * Gets the Interop configuration.
+     */
+    @NonNull
+    Config getInteropConfig();
+
     CameraControlInternal DEFAULT_EMPTY_INSTANCE = new CameraControlInternal() {
         @FlashMode
         @Override
@@ -127,7 +143,6 @@
 
         @Override
         public void cancelAfAeTrigger(boolean cancelAfTrigger, boolean cancelAePrecaptureTrigger) {
-
         }
 
         @NonNull
@@ -171,6 +186,20 @@
         public ListenableFuture<Void> setLinearZoom(float linearZoom) {
             return Futures.immediateFuture(null);
         }
+
+        @Override
+        public void addInteropConfig(@NonNull Config config) {
+        }
+
+        @Override
+        public void clearInteropConfig() {
+        }
+
+        @NonNull
+        @Override
+        public Config getInteropConfig() {
+            return null;
+        }
     };
 
     /** Listener called when CameraControlInternal need to notify event. */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/quirk/IncompleteCameraListQuirk.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/quirk/IncompleteCameraListQuirk.java
index 7f79946..e63e75b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/quirk/IncompleteCameraListQuirk.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/quirk/IncompleteCameraListQuirk.java
@@ -42,7 +42,8 @@
                     "heroqltevzw", "1816", "1814", "1815", "santoni", "htc_oclul", "asus_z01h_1",
                     "vox_alpha_plus", "a5y17ltecan", "x304l", "hero2qltevzw", "a5y17lteskt",
                     "1801", "a5y17lteskt", "1801", "a5y17ltelgt", "herolte", "htc_hiau_ml_tuhl",
-                    "a6plte", "hwtrt-q", "co2_sprout", "h3223", "davinci", "vince", "armor_x5"));
+                    "a6plte", "hwtrt-q", "co2_sprout", "h3223", "davinci", "vince", "armor_x5",
+                    "a2corelte", "j6lte"));
 
     /**
      * @return true if the device might report an incomplete camera id list, otherwise false.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 836fb21..8d0754a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -34,9 +34,11 @@
 import androidx.camera.core.ViewPort;
 import androidx.camera.core.impl.CameraConfig;
 import androidx.camera.core.impl.CameraConfigs;
+import androidx.camera.core.impl.CameraControlInternal;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.UseCaseConfigFactory;
@@ -89,6 +91,10 @@
     @GuardedBy("mLock")
     private boolean mAttached = true;
 
+    // This holds the cached Interop config from CameraControlInternal.
+    @GuardedBy("mLock")
+    private Config mInteropConfig = null;
+
     /**
      * Create a new {@link CameraUseCaseAdapter} instance.
      *
@@ -252,11 +258,14 @@
      * data if they are active.
      *
      * <p> This will start the underlying {@link CameraInternal} instance.
+     *
+     * <p> This will restore the cached Interop config to the {@link CameraInternal}.
      */
     public void attachUseCases() {
         synchronized (mLock) {
             if (!mAttached) {
                 mCameraInternal.attachUseCases(mUseCases);
+                restoreInteropConfig();
 
                 // Notify to update the use case's active state because it may be cleared if the
                 // use case was ever detached from a camera previously.
@@ -273,16 +282,42 @@
      * Detach the UseCases from the {@link CameraInternal} so that the UseCases stop receiving data.
      *
      * <p> This will stop the underlying {@link CameraInternal} instance.
+     *
+     * <p> This will cache the Interop config from the {@link CameraInternal}.
      */
     public void detachUseCases() {
         synchronized (mLock) {
             if (mAttached) {
+                cacheInteropConfig();
                 mCameraInternal.detachUseCases(new ArrayList<>(mUseCases));
                 mAttached = false;
             }
         }
     }
 
+    /**
+     * Restores the cached InteropConfig to the camera.
+     */
+    private void restoreInteropConfig() {
+        synchronized (mLock) {
+            if (mInteropConfig != null) {
+                mCameraInternal.getCameraControlInternal().addInteropConfig(mInteropConfig);
+            }
+        }
+    }
+
+    /**
+     * Caches and clears the InteropConfig from the camera.
+     */
+    private void cacheInteropConfig() {
+        synchronized (mLock) {
+            CameraControlInternal cameraControlInternal =
+                    mCameraInternal.getCameraControlInternal();
+            mInteropConfig = cameraControlInternal.getInteropConfig();
+            cameraControlInternal.clearInteropConfig();
+        }
+    }
+
     private Map<UseCase, Size> calculateSuggestedResolutions(
             @NonNull CameraInfoInternal cameraInfoInternal,
             @NonNull List<UseCase> newUseCases,
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraTest.java b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraTest.java
index 838f616..713ed96 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraTest.java
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager;
@@ -113,6 +115,57 @@
     }
 
     @Test
+    public void lifecycleStart_restoreInteropConfig() {
+        // Set an config to CameraControl internally.
+        Config.Option<Integer> option = Config.Option.create("OPTION_ID", Integer.class);
+        Integer value = 1;
+        MutableOptionsBundle originalConfig = MutableOptionsBundle.create();
+        originalConfig.insertOption(option, value);
+        mFakeCamera.getCameraControlInternal().addInteropConfig(originalConfig);
+
+        mLifecycleCamera = new LifecycleCamera(mLifecycleOwner, mCameraUseCaseAdapter);
+
+        mLifecycleOwner.start();
+
+        // Stop the lifecycle. The original config is cached and the config in CameraControl is
+        // cleared internally.
+        mLifecycleOwner.stop();
+
+        // Set a different config.
+        mFakeCamera.getCameraControlInternal().addInteropConfig(MutableOptionsBundle.create());
+
+        // Starts the lifecycle and the cached config is restored internally.
+        mLifecycleOwner.start();
+
+        // Check the config in CameraControl has the same value as the original config.
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().containsOption(
+                        option)).isTrue();
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().retrieveOption(
+                        option)).isEqualTo(value);
+    }
+
+    @Test
+    public void lifecycleStop_clearInteropConfig() {
+        // Set an config to CameraControl.
+        Config config = MutableOptionsBundle.create();
+        mFakeCamera.getCameraControlInternal().addInteropConfig(config);
+
+        mLifecycleCamera = new LifecycleCamera(mLifecycleOwner, mCameraUseCaseAdapter);
+
+        mLifecycleOwner.start();
+
+        // Stop the lifecycle. The original config is cached and the config in CameraControl is
+        // cleared internally.
+        mLifecycleOwner.stop();
+
+        // Check the config in CameraControl is empty.
+        assertThat(
+                mFakeCamera.getCameraControlInternal().getInteropConfig().listOptions()).isEmpty();
+    }
+
+    @Test
     public void unsuspendOfStartedLifecycle_triggersOnActive() {
         mLifecycleCamera = new LifecycleCamera(mLifecycleOwner, mCameraUseCaseAdapter);
 
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index f64179d..0827987 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -32,6 +32,8 @@
 import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.CameraControlInternal;
 import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.utils.futures.Futures;
 
@@ -52,6 +54,7 @@
     private int mFlashMode = FLASH_MODE_OFF;
     private ArrayList<CaptureConfig> mSubmittedCaptureRequests = new ArrayList<>();
     private OnNewCaptureRequestListener mOnNewCaptureRequestListener;
+    private MutableOptionsBundle mInteropConfig = MutableOptionsBundle.create();
 
     public FakeCameraControl(@NonNull ControlUpdateCallback controlUpdateCallback) {
         mControlUpdateCallback = controlUpdateCallback;
@@ -188,6 +191,26 @@
         return Futures.immediateFuture(null);
     }
 
+    @Override
+    public void addInteropConfig(@NonNull Config config) {
+        for (Config.Option<?> option : config.listOptions()) {
+            @SuppressWarnings("unchecked")
+            Config.Option<Object> objectOpt = (Config.Option<Object>) option;
+            mInteropConfig.insertOption(objectOpt, config.retrieveOption(objectOpt));
+        }
+    }
+
+    @Override
+    public void clearInteropConfig() {
+        mInteropConfig = MutableOptionsBundle.create();
+    }
+
+    @NonNull
+    @Override
+    public Config getInteropConfig() {
+        return mInteropConfig;
+    }
+
     /** A listener which are used to notify when there are new submitted capture requests */
     public interface OnNewCaptureRequestListener {
         /** Called when there are new submitted capture request */
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 4b3be08..424af0f 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -23,6 +23,7 @@
 import android.os.Looper;
 import android.view.Display;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -63,6 +64,8 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -80,13 +83,13 @@
  * tap-to-focus and pinch-to-zoom features.
  *
  * <p> This class provides features of 4 {@link UseCase}s: {@link Preview}, {@link ImageCapture},
- * {@link ImageAnalysis} and video capture. {@link Preview} is required and always enabled.
- * {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. Video capture is
- * disabled by default because it might conflict with other use cases, especially on lower end
- * devices. It might be necessary to disable {@link ImageCapture} and/or {@link ImageAnalysis}
- * before the video feature can be enabled. Disabling/enabling {@link UseCase}s freezes the
- * preview for a short period of time. To avoid the glitch, the {@link UseCase}s need to be
- * enabled/disabled before the controller is set on {@link PreviewView}.
+ * {@link ImageAnalysis} and an experimental video capture. {@link Preview} is required and always
+ * enabled. {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. The video
+ * capture feature is experimental. It's disabled by default because it might conflict with other
+ * use cases, especially on lower end devices. It might be necessary to disable {@link ImageCapture}
+ * and/or {@link ImageAnalysis} before the video capture feature can be enabled. Disabling/enabling
+ * {@link UseCase}s freezes the preview for a short period of time. To avoid the glitch, the
+ * {@link UseCase}s need to be enabled/disabled before the controller is set on {@link PreviewView}.
  */
 public abstract class CameraController {
 
@@ -104,21 +107,29 @@
     private static final float AE_SIZE = AF_SIZE * 1.5f;
 
     /**
-     * Bitmask option to enable {@link android.media.Image}. In {@link #setEnabledUseCases}, if
+     * Bitmask options to enable/disable use cases.
+     */
+    @UseExperimental(markerClass = ExperimentalVideo.class)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = { IMAGE_CAPTURE, IMAGE_ANALYSIS, VIDEO_CAPTURE })
+    public @interface UseCases { }
+
+    /**
+     * Bitmask option to enable {@link ImageCapture}. In {@link #setEnabledUseCases}, if
      * (enabledUseCases & IMAGE_CAPTURE) != 0, then controller will enable image capture features.
      */
-    public static final int IMAGE_CAPTURE = 0b1;
+    public static final int IMAGE_CAPTURE = 1;
     /**
      * Bitmask option to enable {@link ImageAnalysis}. In {@link #setEnabledUseCases}, if
      * (enabledUseCases & IMAGE_ANALYSIS) != 0, then controller will enable image analysis features.
      */
-    public static final int IMAGE_ANALYSIS = 0b10;
+    public static final int IMAGE_ANALYSIS = 1 << 1;
     /**
      * Bitmask option to enable video capture use case. In {@link #setEnabledUseCases}, if
      * (enabledUseCases & VIDEO_CAPTURE) != 0, then controller will enable video capture features.
      */
     @ExperimentalVideo
-    public static final int VIDEO_CAPTURE = 0b100;
+    public static final int VIDEO_CAPTURE = 1 << 2;
 
     CameraSelector mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
 
@@ -308,7 +319,7 @@
      */
     @MainThread
     @UseExperimental(markerClass = ExperimentalVideo.class)
-    public void setEnabledUseCases(int enabledUseCases) {
+    public void setEnabledUseCases(@UseCases int enabledUseCases) {
         Threads.checkMainThread();
         if (enabledUseCases == mEnabledUseCases) {
             return;
diff --git a/collection/collection-benchmark/src/androidTest/AndroidManifest.xml b/collection/collection-benchmark/src/androidTest/AndroidManifest.xml
index 1664e8d..95836aa 100644
--- a/collection/collection-benchmark/src/androidTest/AndroidManifest.xml
+++ b/collection/collection-benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/UnionCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/UnionCheckerTests.kt
deleted file mode 100644
index 3e4c32b..0000000
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/UnionCheckerTests.kt
+++ /dev/null
@@ -1,106 +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.compose.compiler.plugins.kotlin.analysis
-
-import androidx.compose.compiler.plugins.kotlin.AbstractComposeDiagnosticsTest
-
-class UnionCheckerTests : AbstractComposeDiagnosticsTest() {
-
-    fun testUnionTypeReporting001() {
-        doTest(
-            """
-            import androidx.compose.runtime.*;
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable fun foo(value: @UnionType(Int::class, String::class) Any) {
-                System.out.println(value)
-            }
-
-            @Composable
-            fun bar() {
-                foo(value=1)
-                foo(value="1")
-                foo(value=<!ILLEGAL_ASSIGN_TO_UNIONTYPE!>1f<!>)
-            }
-        """
-        )
-    }
-
-    fun testUnionTypeReporting002() {
-        doTest(
-            """
-            import androidx.compose.runtime.*;
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable fun foo(value: @UnionType(Int::class, String::class) Any) {
-                System.out.println(value)
-            }
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable
-            fun bar(value: @UnionType(Int::class, String::class) Any) {
-                foo(value)
-            }
-        """
-        )
-    }
-
-    fun testUnionTypeReporting003() {
-        doTest(
-            """
-            import androidx.compose.runtime.*;
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable fun foo(value: @UnionType(Int::class, String::class, Float::class) Any) {
-                System.out.println(value)
-            }
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable
-            fun bar(value: @UnionType(Int::class, String::class) Any) {
-                foo(value)
-            }
-        """
-        )
-    }
-
-    fun testUnionTypeReporting004() {
-        doTest(
-            """
-            import androidx.compose.runtime.*;
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable fun foo(value: @UnionType(Int::class, String::class) Any) {
-                System.out.println(value)
-            }
-
-            @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-            @OptIn(androidx.compose.runtime.ExperimentalComposeApi::class)
-            @Composable
-            fun bar(value: @UnionType(Int::class, String::class, Float::class) Any) {
-                foo(<!ILLEGAL_ASSIGN_TO_UNIONTYPE!>value<!>)
-            }
-        """
-        )
-    }
-}
diff --git a/compose/compiler/compiler-hosted/lint-baseline.xml b/compose/compiler/compiler-hosted/lint-baseline.xml
new file mode 100644
index 0000000..c77cb6f
--- /dev/null
+++ b/compose/compiler/compiler-hosted/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-alpha06" client="gradle" version="4.2.0-alpha06">
+
+    <issue
+        id="IllegalExperimentalApiUsage"
+        message="Experimental/OptIn APIs should only be used from within the same library or libraries within the same requireSameVersion group"
+        errorLine1="                ComposeTypeResolutionInterceptorExtension()"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt"
+            line="145"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="IllegalExperimentalApiUsage"
+        message="Experimental/OptIn APIs should only be used from within the same library or libraries within the same requireSameVersion group"
+        errorLine1="open class ComposeTypeResolutionInterceptorExtension : TypeResolutionInterceptorExtension {"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt"
+            line="38"
+            column="56"/>
+    </issue>
+
+</issues>
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 4d3f524..f1cc573 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -132,10 +132,6 @@
                 project,
                 ComposableDeclarationChecker()
             )
-            StorageComponentContainerContributor.registerExtension(
-                project,
-                UnionAnnotationCheckerProvider()
-            )
             ComposeDiagnosticSuppressor.registerExtension(
                 project,
                 ComposeDiagnosticSuppressor()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/UnionAnnotationChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/UnionAnnotationChecker.kt
deleted file mode 100644
index 9341b22..0000000
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/UnionAnnotationChecker.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.compiler.plugins.kotlin
-
-import org.jetbrains.kotlin.types.TypeUtils
-import org.jetbrains.kotlin.container.StorageComponentContainer
-import org.jetbrains.kotlin.container.useInstance
-import org.jetbrains.kotlin.descriptors.ModuleDescriptor
-import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
-import org.jetbrains.kotlin.name.Name
-import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.platform.TargetPlatform
-import org.jetbrains.kotlin.platform.jvm.isJvm
-import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
-import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
-import org.jetbrains.kotlin.resolve.constants.ArrayValue
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
-
-open class UnionAnnotationCheckerProvider() : StorageComponentContainerContributor {
-    override fun registerModuleComponents(
-        container: StorageComponentContainer,
-        platform: TargetPlatform,
-        moduleDescriptor: ModuleDescriptor
-    ) {
-        if (!platform.isJvm()) return
-        container.useInstance(
-            UnionAnnotationChecker(
-                moduleDescriptor
-            )
-        )
-    }
-}
-
-open class UnionAnnotationChecker(val moduleDescriptor: ModuleDescriptor) : AdditionalTypeChecker {
-    companion object {
-        val UNIONTYPE_ANNOTATION_NAME =
-            ComposeFqNames.fqNameFor("UnionType")
-    }
-
-    override fun checkType(
-        expression: KtExpression,
-        expressionType: KotlinType,
-        expressionTypeWithSmartCast: KotlinType,
-        c: ResolutionContext<*>
-    ) {
-        val expectedType = c.expectedType
-        if (TypeUtils.noExpectedType(expectedType)) return
-
-        if (!expectedType.annotations.hasAnnotation(UNIONTYPE_ANNOTATION_NAME) &&
-            !expressionTypeWithSmartCast.annotations.hasAnnotation(UNIONTYPE_ANNOTATION_NAME)
-        ) {
-            return
-        }
-
-        val expressionTypes = getUnionTypes(expressionTypeWithSmartCast)
-        val permittedTypes = getUnionTypes(expectedType)
-
-        outer@ for (potentialExpressionType in expressionTypes) {
-            for (permittedType in permittedTypes) {
-                if (KotlinTypeChecker.DEFAULT.isSubtypeOf(potentialExpressionType, permittedType))
-                    continue@outer
-            }
-            c.trace.report(
-                ComposeErrors.ILLEGAL_ASSIGN_TO_UNIONTYPE.on(
-                    expression,
-                    listOf(potentialExpressionType),
-                    permittedTypes
-                )
-            )
-            return
-        }
-    }
-
-    private fun getUnionTypes(type: KotlinType): List<KotlinType> {
-        val annotation =
-            type.annotations.findAnnotation(UNIONTYPE_ANNOTATION_NAME) ?: return listOf(type)
-        val types = annotation.allValueArguments.get(Name.identifier("types")) as ArrayValue
-        return types.value.map { it.getType(moduleDescriptor).arguments.single().type }
-    }
-}
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 48a9972..59e8300 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
@@ -18,22 +18,30 @@
 import androidx.compose.animation.animate
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.desktop.AppWindow
+import androidx.compose.desktop.DesktopMaterialTheme
 import androidx.compose.desktop.Window
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Image
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.ScrollableColumn
-import androidx.compose.material.Text
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.VerticalScrollbar
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.rememberScrollbarAdapter
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.text.InlineTextContent
 import androidx.compose.foundation.text.appendInlineContent
@@ -86,6 +94,13 @@
 
 fun main() {
     Window(title, IntSize(1024, 850)) {
+        App()
+    }
+}
+
+@Composable
+private fun App() {
+    DesktopMaterialTheme {
         Scaffold(
             topBar = {
                 TopAppBar(
@@ -125,15 +140,26 @@
     }
 }
 
+@Composable
+private fun LeftColumn(modifier: Modifier) = Box(modifier.fillMaxSize()) {
+    val state = rememberScrollState(0f)
+    ScrollableContent(state)
+
+    VerticalScrollbar(
+        rememberScrollbarAdapter(state),
+        Modifier.align(Alignment.CenterEnd).fillMaxHeight()
+    )
+}
+
 @OptIn(ExperimentalKeyInput::class)
 @Composable
-private fun LeftColumn(modifier: Modifier) = Column(modifier) {
+private fun ScrollableContent(scrollState: ScrollState) {
     val amount = remember { mutableStateOf(0) }
     val animation = remember { mutableStateOf(true) }
     val text = remember {
         mutableStateOf("Hello \uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0\nПривет")
     }
-    ScrollableColumn(Modifier.fillMaxSize()) {
+    ScrollableColumn(Modifier.fillMaxSize(), scrollState) {
         Text(
             text = "Привет! 你好! Desktop Compose ${amount.value}",
             color = Color.Black,
@@ -353,9 +379,21 @@
     }
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
-private fun RightColumn(modifier: Modifier) = LazyColumn(modifier.drawLayer(alpha = 0.5f)) {
-    items((1..10000).toList()) { x ->
-        Text(x.toString(), Modifier.drawLayer(alpha = 0.5f))
+private fun RightColumn(modifier: Modifier) = Box {
+    val state = rememberLazyListState()
+    val itemCount = 100000
+    val itemHeight = 20.dp
+
+    LazyColumn(modifier.drawLayer(alpha = 0.5f), state = state) {
+        items((1..itemCount).toList()) { x ->
+            Text(x.toString(), Modifier.drawLayer(alpha = 0.5f).height(itemHeight))
+        }
     }
+
+    VerticalScrollbar(
+        rememberScrollbarAdapter(state, itemCount, itemHeight),
+        Modifier.align(Alignment.CenterEnd)
+    )
 }
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/DesktopTheme.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/DesktopTheme.kt
new file mode 100644
index 0000000..b822bf6
--- /dev/null
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/DesktopTheme.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.desktop
+
+import androidx.compose.foundation.ScrollbarStyle
+import androidx.compose.foundation.ScrollbarStyleAmbient
+import androidx.compose.material.Colors
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Shapes
+import androidx.compose.material.Typography
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun DesktopMaterialTheme(
+    colors: Colors = MaterialTheme.colors,
+    typography: Typography = MaterialTheme.typography,
+    shapes: Shapes = MaterialTheme.shapes,
+    content: @Composable () -> Unit
+) = MaterialTheme(
+    colors,
+    typography,
+    shapes
+) {
+    DesktopTheme(content = content)
+}
+
+@Composable
+fun DesktopTheme(
+    scrollbar: ScrollbarStyle = ScrollbarStyle(
+        minimalHeight = 16.dp,
+        thickness = 8.dp,
+        shape = MaterialTheme.shapes.small,
+        hoverDurationMillis = 300,
+        unhoverColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
+        hoverColor = MaterialTheme.colors.onSurface.copy(alpha = 0.50f)
+    ),
+    content: @Composable () -> Unit
+) = Providers(
+    ScrollbarStyleAmbient provides scrollbar,
+    children = content
+)
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
index 28e0534..bef1290 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
@@ -153,7 +153,6 @@
  * @sample androidx.compose.foundation.layout.samples.PaddingFromBaselineSampleDp
  */
 @Stable
-@Suppress("ModifierInspectorInfo")
 fun Modifier.paddingFromBaseline(top: Dp = Dp.Unspecified, bottom: Dp = Dp.Unspecified) = this
     .then(if (bottom != Dp.Unspecified) paddingFrom(LastBaseline, after = bottom) else Modifier)
     .then(if (top != Dp.Unspecified) paddingFrom(FirstBaseline, before = top) else Modifier)
@@ -171,7 +170,6 @@
  * @sample androidx.compose.foundation.layout.samples.PaddingFromBaselineSampleTextUnit
  */
 @Stable
-@Suppress("ModifierInspectorInfo")
 fun Modifier.paddingFromBaseline(
     top: TextUnit = TextUnit.Inherit,
     bottom: TextUnit = TextUnit.Inherit
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index dc0fe13..c8bc665 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -294,9 +294,9 @@
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
-    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier);
+    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public final class LazyListKt {
@@ -466,7 +466,6 @@
     method public androidx.compose.ui.text.input.KeyboardCapitalization getCapitalization();
     method public androidx.compose.ui.text.input.ImeAction getImeAction();
     method public androidx.compose.ui.text.input.KeyboardType getKeyboardType();
-    method public androidx.compose.ui.text.input.ImeOptions toImeOptions(optional boolean singleLine);
     property public final boolean autoCorrect;
     property public final androidx.compose.ui.text.input.KeyboardCapitalization capitalization;
     property public final androidx.compose.ui.text.input.ImeAction imeAction;
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index dc0fe13..c8bc665 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -294,9 +294,9 @@
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
-    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier);
+    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public final class LazyListKt {
@@ -466,7 +466,6 @@
     method public androidx.compose.ui.text.input.KeyboardCapitalization getCapitalization();
     method public androidx.compose.ui.text.input.ImeAction getImeAction();
     method public androidx.compose.ui.text.input.KeyboardType getKeyboardType();
-    method public androidx.compose.ui.text.input.ImeOptions toImeOptions(optional boolean singleLine);
     property public final boolean autoCorrect;
     property public final androidx.compose.ui.text.input.KeyboardCapitalization capitalization;
     property public final androidx.compose.ui.text.input.ImeAction imeAction;
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index dc0fe13..c8bc665 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -294,9 +294,9 @@
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
-    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier);
-    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier);
+    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public final class LazyListKt {
@@ -466,7 +466,6 @@
     method public androidx.compose.ui.text.input.KeyboardCapitalization getCapitalization();
     method public androidx.compose.ui.text.input.ImeAction getImeAction();
     method public androidx.compose.ui.text.input.KeyboardType getKeyboardType();
-    method public androidx.compose.ui.text.input.ImeOptions toImeOptions(optional boolean singleLine);
     property public final boolean autoCorrect;
     property public final androidx.compose.ui.text.input.KeyboardCapitalization capitalization;
     property public final androidx.compose.ui.text.input.ImeAction imeAction;
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 47e84a1..093b8b2 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -56,6 +56,7 @@
         testImplementation(JUNIT)
         testImplementation(TRUTH)
 
+        androidTestImplementation project(":compose:test-utils")
         androidTestImplementation project(':ui:ui-test')
         androidTestImplementation project(":compose:ui:ui-test-font")
         androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
@@ -109,6 +110,7 @@
             }
 
             androidAndroidTest.dependencies {
+                implementation project(":compose:test-utils")
                 implementation project(':ui:ui-test')
                 implementation project(":compose:ui:ui-test-font")
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
index 80675b6..cd26a9e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BackgroundTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -31,8 +32,7 @@
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -78,7 +78,7 @@
                 }
             }
         }
-        val bitmap = rule.onNodeWithTag(contentTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(contentTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Magenta,
@@ -104,7 +104,7 @@
                 }
             }
         }
-        val bitmap = rule.onNodeWithTag(contentTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(contentTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Magenta,
@@ -126,7 +126,7 @@
                 )
             }
         }
-        val bitmap = rule.onNodeWithTag(contentTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(contentTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Magenta,
@@ -150,7 +150,7 @@
                 )
             }
         }
-        val bitmap = rule.onNodeWithTag(contentTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(contentTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Magenta,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BorderTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BorderTest.kt
index 3ca107a..4b23f01 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BorderTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/BorderTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -29,8 +30,7 @@
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -71,7 +71,7 @@
                 ) {}
             }
         }
-        val bitmap = rule.onNodeWithTag(testTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(testTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Red,
@@ -98,7 +98,7 @@
                 ) {}
             }
         }
-        val bitmap = rule.onNodeWithTag(testTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(testTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.Red,
@@ -122,7 +122,7 @@
                 ) {}
             }
         }
-        val bitmap = rule.onNodeWithTag(testTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(testTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.White,
@@ -144,7 +144,7 @@
                 ) {}
             }
         }
-        val bitmap = rule.onNodeWithTag(testTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(testTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.White,
@@ -169,7 +169,7 @@
                 }
             }
         }
-        val bitmap = rule.onNodeWithTag(testTag).captureToBitmap()
+        val bitmap = rule.onNodeWithTag(testTag).captureToImage()
         bitmap.assertShape(
             density = rule.density,
             backgroundColor = Color.White,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/CanvasTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/CanvasTest.kt
index e50e62a..1a61f63 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/CanvasTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/CanvasTest.kt
@@ -21,17 +21,18 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.test.assertHeightIsEqualTo
-import androidx.compose.ui.test.assertShape
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.dp
@@ -84,7 +85,7 @@
         val paintBoxColor = Color.Red.toArgb()
         val containerBgColor = Color.White.toArgb()
         val strokeOffset = (strokeWidth / 2).toInt() + 3
-        rule.onRoot().captureToBitmap().apply {
+        rule.onRoot().captureToImage().asAndroidBitmap().apply {
             val imageStartX = width / 2 - boxWidth / 2
             val imageStartY = height / 2 - boxHeight / 2
 
@@ -180,7 +181,7 @@
         }
             .assertWidthIsEqualTo(100.dp)
             .assertHeightIsEqualTo(100.dp)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.Red,
@@ -203,7 +204,7 @@
         }
             .assertWidthIsEqualTo(100.dp)
             .assertHeightIsEqualTo(100.dp)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.Red,
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 a274c71..06ce451 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
@@ -34,19 +34,23 @@
 import androidx.compose.ui.graphics.ImageAsset
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
 import androidx.compose.ui.graphics.painter.ImagePainter
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.loadVectorResource
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.testutils.assertPixels
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -111,7 +115,7 @@
         val bgColorArgb = bgColor.toArgb()
         val pathArgb = pathColor.toArgb()
 
-        rule.onNodeWithTag(contentTag).captureToBitmap().apply {
+        rule.onNodeWithTag(contentTag).captureToImage().asAndroidBitmap().apply {
             val imageStartX = width / 2 - imageWidth / 2
             val imageStartY = height / 2 - imageHeight / 2
             Assert.assertEquals(bgColorArgb, getPixel(imageStartX + 2, imageStartY))
@@ -162,7 +166,7 @@
         val bgColorArgb = bgColor.toArgb()
         val pathArgb = pathColor.toArgb()
 
-        rule.onRoot().captureToBitmap().apply {
+        rule.onRoot().captureToImage().asAndroidBitmap().apply {
             val imageStartX = width / 2 - subsectionWidth / 2
             val imageStartY = height / 2 - subsectionHeight / 2
             Assert.assertEquals(bgColorArgb, getPixel(imageStartX + 2, imageStartY))
@@ -261,7 +265,7 @@
 
         val bgColorArgb = bgColor.toArgb()
         val pathArgb = pathColor.toArgb()
-        rule.onNodeWithTag(contentTag).captureToBitmap().apply {
+        rule.onNodeWithTag(contentTag).captureToImage().asAndroidBitmap().apply {
             val imageStartX = width / 2 - imageComposableWidth / 2
             val imageStartY = height / 2 - imageComposableHeight / 2
             Assert.assertEquals(bgColorArgb, getPixel(imageStartX + 5, imageStartY))
@@ -292,6 +296,46 @@
     }
 
     @Test
+    fun testImageScalesNonuniformly() {
+        val imageComposableWidth = imageWidth * 3
+        val imageComposableHeight = imageHeight * 7
+
+        rule.setContent {
+            val density = DensityAmbient.current
+            val size = (containerSize * 2 / density.density).dp
+            val imageAsset = ImageAsset(imageWidth, imageHeight)
+            CanvasDrawScope().draw(
+                density,
+                LayoutDirection.Ltr,
+                Canvas(imageAsset),
+                Size(imageWidth.toFloat(), imageHeight.toFloat())
+            ) {
+                drawRect(color = Color.Blue)
+            }
+            Box(
+                Modifier.preferredSize(size)
+                    .background(color = Color.White)
+                    .wrapContentSize(Alignment.Center)
+            ) {
+                Image(
+                    asset = imageAsset,
+                    modifier = Modifier
+                        .testTag(contentTag)
+                        .preferredSize(
+                            (imageComposableWidth / density.density).dp,
+                            (imageComposableHeight / density.density).dp
+                        ),
+                    // Scale the image non-uniformly within the bounds of the composable
+                    contentScale = ContentScale.FillBounds,
+                    alignment = Alignment.BottomEnd
+                )
+            }
+        }
+
+        rule.onNodeWithTag(contentTag).captureToImage().assertPixels { Color.Blue }
+    }
+
+    @Test
     fun testImageFixedSizeAlignedBottomEnd() {
         val imageComposableWidth = imageWidth * 2
         val imageComposableHeight = imageHeight * 2
@@ -322,7 +366,7 @@
 
         val bgColorArgb = bgColor.toArgb()
         val pathArgb = pathColor.toArgb()
-        rule.onNodeWithTag(contentTag).captureToBitmap().apply {
+        rule.onNodeWithTag(contentTag).captureToImage().asAndroidBitmap().apply {
             val composableEndX = width / 2 + imageComposableWidth / 2
             val composableEndY = height / 2 + imageComposableHeight / 2
             val imageStartX = composableEndX - imageWidth
@@ -384,7 +428,7 @@
 
         val imageColor = Color.Red.toArgb()
         val containerBgColor = Color.White.toArgb()
-        rule.onRoot().captureToBitmap().apply {
+        rule.onRoot().captureToImage().asAndroidBitmap().apply {
             val imageStartX = width / 2 - boxWidth / 2
             val imageStartY = height / 2 - boxHeight / 2
             Assert.assertEquals(containerBgColor, getPixel(imageStartX - 1, imageStartY - 1))
@@ -463,7 +507,7 @@
             )
         }
 
-        rule.onNodeWithTag(testTag).captureToBitmap().apply {
+        rule.onNodeWithTag(testTag).captureToImage().asAndroidBitmap().apply {
             Assert.assertEquals(100, width)
             Assert.assertEquals(50, height)
             Assert.assertEquals(Color.Blue.toArgb(), getPixel(24, height / 2))
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
index 9930f8d..1455b85 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
@@ -28,6 +28,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.InspectableValue
@@ -38,8 +39,7 @@
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -830,7 +830,7 @@
         rowHeight: Int = 5
     ) {
         rule.onNodeWithTag(scrollerTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(width, height)) { pos ->
                 val colorIndex = (offset + pos.y) / rowHeight
                 colors[colorIndex]
@@ -847,7 +847,7 @@
         val scrollerWidth = colors.size * defaultCellSize
         val absoluteOffset = if (checkInRtl) scrollerWidth - width - offset else offset
         rule.onNodeWithTag(scrollerTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(width, height)) { pos ->
                 val colorIndex = (absoluteOffset + pos.x) / defaultCellSize
                 if (checkInRtl) colors[colors.size - 1 - colorIndex] else colors[colorIndex]
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
index d8949b4..929634e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation
 
-import android.graphics.Bitmap
 import android.os.Build
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
@@ -25,15 +24,16 @@
 import androidx.compose.foundation.text.blinkingCursorEnabled
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertPixels
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.isFocused
 import androidx.compose.ui.focusObserver
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageAsset
 import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.hasInputMethodsSupport
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.performClick
@@ -95,7 +95,7 @@
         rule.clockTestRule.advanceClock(100)
         with(rule.density) {
             rule.onNode(hasInputMethodsSupport())
-                .captureToBitmap()
+                .captureToImage()
                 .assertCursor(2.dp, this)
         }
     }
@@ -133,14 +133,14 @@
         rule.clockTestRule.advanceClock(100)
         with(rule.density) {
             rule.onNode(hasInputMethodsSupport())
-                .captureToBitmap()
+                .captureToImage()
                 .assertCursor(2.dp, this)
         }
 
         // cursor invisible during next 500 ms
         rule.clockTestRule.advanceClock(700)
         rule.onNode(hasInputMethodsSupport())
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -182,7 +182,7 @@
         // no cursor when usually shown
         rule.clockTestRule.advanceClock(100)
         rule.onNode(hasInputMethodsSupport())
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -194,7 +194,7 @@
         // no cursor when should be no cursor
         rule.clockTestRule.advanceClock(700)
         rule.onNode(hasInputMethodsSupport())
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -249,12 +249,12 @@
         rule.clockTestRule.advanceClock(400)
         with(rule.density) {
             rule.onNode(hasInputMethodsSupport())
-                .captureToBitmap()
+                .captureToImage()
                 .assertCursor(2.dp, this)
         }
     }
 
-    private fun Bitmap.assertCursor(cursorWidth: Dp, density: Density) {
+    private fun ImageAsset.assertCursor(cursorWidth: Dp, density: Density) {
         val cursorWidthPx = (with(density) { cursorWidth.toIntPx() })
         val width = width
         val height = height
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
index 8b1c910..b5d7c81 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
@@ -34,6 +34,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.isFocused
@@ -51,9 +52,8 @@
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
-import androidx.compose.ui.test.assertShape
 import androidx.compose.ui.test.assertTextEquals
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.hasImeAction
 import androidx.compose.ui.test.hasInputMethodsSupport
 import androidx.compose.ui.test.isFocused
@@ -404,7 +404,7 @@
         }
 
         rule.onNode(hasInputMethodsSupport())
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnForTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnForTest.kt
index 41d5767..1dbed00 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnForTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnForTest.kt
@@ -548,6 +548,54 @@
     }
 
     @Test
+    fun itemFillingParentWidthFraction() {
+        rule.setContent {
+            LazyColumnFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.fillParentMaxWidth(0.6f).height(50.dp).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(60.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeightFraction() {
+        rule.setContent {
+            LazyColumnFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.width(50.dp).fillParentMaxHeight(0.2f).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(30.dp)
+    }
+
+    @Test
+    fun itemFillingParentSizeFraction() {
+        rule.setContent {
+            LazyColumnFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.fillParentMaxSize(0.1f).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(10.dp)
+            .assertHeightIsEqualTo(15.dp)
+    }
+
+    @Test
     fun itemFillingParentSizeParentResized() {
         var parentSize by mutableStateOf(100.dp)
         rule.setContent {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowForTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowForTest.kt
index 0d0e062..8b142aa 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowForTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowForTest.kt
@@ -384,6 +384,54 @@
     }
 
     @Test
+    fun itemFillingParentWidthFraction() {
+        rule.setContent {
+            LazyRowFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.fillParentMaxWidth(0.7f).height(50.dp).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(70.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeightFraction() {
+        rule.setContent {
+            LazyRowFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.width(50.dp).fillParentMaxHeight(0.3f).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(45.dp)
+    }
+
+    @Test
+    fun itemFillingParentSizeFraction() {
+        rule.setContent {
+            LazyRowFor(
+                items = listOf(0),
+                modifier = Modifier.size(width = 100.dp, height = 150.dp)
+            ) {
+                Spacer(Modifier.fillParentMaxSize(0.5f).testTag(firstItemTag))
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(75.dp)
+    }
+
+    @Test
     fun itemFillingParentSizeParentResized() {
         var parentSize by mutableStateOf(100.dp)
         rule.setContent {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/CachingItemContentFactory.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/CachingItemContentFactory.kt
index 0c9b54d..e564146 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/CachingItemContentFactory.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/CachingItemContentFactory.kt
@@ -16,9 +16,9 @@
 
 package androidx.compose.foundation.lazy
 
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.Constraints
@@ -86,7 +86,14 @@
     val maxWidth: Dp,
     val maxHeight: Dp
 ) : LazyItemScope {
-    override fun Modifier.fillParentMaxSize() = size(maxWidth, maxHeight)
-    override fun Modifier.fillParentMaxWidth() = width(maxWidth)
-    override fun Modifier.fillParentMaxHeight() = height(maxHeight)
+    override fun Modifier.fillParentMaxSize(fraction: Float) = preferredSize(
+        maxWidth * fraction,
+        maxHeight * fraction
+    )
+
+    override fun Modifier.fillParentMaxWidth(fraction: Float) =
+        preferredWidth(maxWidth * fraction)
+
+    override fun Modifier.fillParentMaxHeight(fraction: Float) =
+        preferredHeight(maxHeight * fraction)
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
index 03870b4..fc313c0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.util.annotation.FloatRange
 
 /**
  * Receiver scope being used by the item content parameter of LazyColumn/Row.
@@ -28,31 +29,41 @@
     /**
      * Have the content fill the [Constraints.maxWidth] and [Constraints.maxHeight] of the parent
      * measurement constraints by setting the [minimum width][Constraints.minWidth] to be equal to the
-     * [maximum width][Constraints.maxWidth] and the [minimum height][Constraints.minHeight] to be
-     * equal to the [maximum height][Constraints.maxHeight].
+     * [maximum width][Constraints.maxWidth] multiplied by [fraction] and the [minimum
+     * height][Constraints.minHeight] to be equal to the [maximum height][Constraints.maxHeight]
+     * multiplied by [fraction]. Note that, by default, the [fraction] is 1, so the modifier will
+     * make the content fill the whole available space.
      *
      * Regular [Modifier.fillMaxSize] can't work inside the scrolling layouts as the items are
      * measured with [Constraints.Infinity] as the constraints for the main axis.
      */
-    fun Modifier.fillParentMaxSize(): Modifier
+    fun Modifier.fillParentMaxSize(
+        @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f
+    ): Modifier
 
     /**
      * Have the content fill the [Constraints.maxWidth] of the parent measurement constraints
      * by setting the [minimum width][Constraints.minWidth] to be equal to the
-     * [maximum width][Constraints.maxWidth].
+     * [maximum width][Constraints.maxWidth] multiplied by [fraction]. Note that, by default, the
+     * [fraction] is 1, so the modifier will make the content fill the whole parent width.
      *
      * Regular [Modifier.fillMaxWidth] can't work inside the scrolling horizontally layouts as the
      * items are measured with [Constraints.Infinity] as the constraints for the main axis.
      */
-    fun Modifier.fillParentMaxWidth(): Modifier
+    fun Modifier.fillParentMaxWidth(
+        @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f
+    ): Modifier
 
     /**
      * Have the content fill the [Constraints.maxHeight] of the incoming measurement constraints
      * by setting the [minimum height][Constraints.minHeight] to be equal to the
-     * [maximum height][Constraints.maxHeight].
+     * [maximum height][Constraints.maxHeight] multiplied by [fraction]. Note that, by default, the
+     * [fraction] is 1, so the modifier will make the content fill the whole parent height.
      *
      * Regular [Modifier.fillMaxHeight] can't work inside the scrolling vertically layouts as the
      * items are measured with [Constraints.Infinity] as the constraints for the main axis.
      */
-    fun Modifier.fillParentMaxHeight(): Modifier
+    fun Modifier.fillParentMaxHeight(
+        @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f
+    ): Modifier
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
index 6ff62b6..fe0c0b0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
@@ -44,12 +44,15 @@
  */
 @Immutable
 data class KeyboardOptions constructor(
-    val capitalization: KeyboardCapitalization = ImeOptions.Default.capitalization,
-    val autoCorrect: Boolean = ImeOptions.Default.autoCorrect,
-    val keyboardType: KeyboardType = ImeOptions.Default.keyboardType,
-    val imeAction: ImeAction = ImeOptions.Default.imeAction,
+    val capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
+    val autoCorrect: Boolean = true,
+    val keyboardType: KeyboardType = KeyboardType.Text,
+    val imeAction: ImeAction = ImeAction.Unspecified
 ) {
     companion object {
+        /**
+         * Default [KeyboardOptions]. Please see parameter descriptions for default values.
+         */
         val Default = KeyboardOptions()
     }
 
@@ -59,7 +62,7 @@
      *
      * @param singleLine see [ImeOptions.singleLine]
      */
-    fun toImeOptions(singleLine: Boolean = ImeOptions.Default.singleLine) = ImeOptions(
+    internal fun toImeOptions(singleLine: Boolean = ImeOptions.Default.singleLine) = ImeOptions(
         singleLine = singleLine,
         capitalization = capitalization,
         autoCorrect = autoCorrect,
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.kt
new file mode 100644
index 0000000..295fd59
--- /dev/null
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.kt
@@ -0,0 +1,502 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import androidx.compose.animation.animate
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.staticAmbientOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.DragObserver
+import androidx.compose.ui.gesture.PressTimeout
+import androidx.compose.ui.gesture.pressIndicatorGestureFilter
+import androidx.compose.ui.gesture.rawDragGestureFilter
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.input.pointer.pointerMoveFilter
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.MeasuringIntrinsicsMeasureBlocks
+import androidx.compose.ui.node.ExperimentalLayoutNodeApi
+import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.inMilliseconds
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import kotlin.math.sign
+
+/**
+ * Ambient used to pass [ScrollbarStyle] down the tree.
+ * This value is typically set in some "Theme" composable function
+ * (DesktopTheme, MaterialTheme)
+ */
+val ScrollbarStyleAmbient = staticAmbientOf { defaultScrollbarStyle() }
+
+/**
+ * Defines visual style of scrollbars (thickness, shapes, colors, etc).
+ * Can be passed as a parameter of scrollbar through [ScrollbarStyleAmbient]
+ */
+@Immutable
+data class ScrollbarStyle(
+    val minimalHeight: Dp,
+    val thickness: Dp,
+    val shape: Shape,
+    val hoverDurationMillis: Int,
+    val unhoverColor: Color,
+    val hoverColor: Color
+)
+
+/**
+ * Simple default [ScrollbarStyle] without hover effects and without applying MaterialTheme.
+ */
+fun defaultScrollbarStyle() = ScrollbarStyle(
+    minimalHeight = 16.dp,
+    thickness = 8.dp,
+    shape = RectangleShape,
+    hoverDurationMillis = 0,
+    unhoverColor = Color.Black.copy(alpha = 0.12f),
+    hoverColor = Color.Black.copy(alpha = 0.12f)
+)
+
+/**
+ * Vertical scrollbar that can be attached to some scrollable
+ * component (ScrollableColumn, LazyColumnFor) and share common state with it.
+ *
+ * Can be placed independently.
+ *
+ * Example:
+ *     val state = rememberScrollState(0f)
+ *
+ *     Box(Modifier.fillMaxSize()) {
+ *         ScrollableColumn(state = state) {
+ *             ...
+ *         }
+ *
+ *         VerticalScrollbar(
+ *             Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
+ *             rememberScrollbarAdapter(state)
+ *         )
+ *     }
+ *
+ * @param adapter [ScrollbarAdapter] that will be used to communicate with scrollable component
+ * @param modifier the modifier to apply to this layout
+ * @param style [ScrollbarStyle] to define visual style of scrollbar
+ * @param interactionState [InteractionState] that will be updated when the element with this
+ * state is being dragged, using [Interaction.Dragged]
+ */
+@Composable
+fun VerticalScrollbar(
+    adapter: ScrollbarAdapter,
+    modifier: Modifier = Modifier,
+    style: ScrollbarStyle = ScrollbarStyleAmbient.current,
+    interactionState: InteractionState = remember { InteractionState() }
+) = Scrollbar(
+    adapter,
+    modifier,
+    style,
+    interactionState,
+    isVertical = true
+)
+
+/**
+ * Horizontal scrollbar that can be attached to some scrollable
+ * component (ScrollableRow, LazyRowFor) and share common state with it.
+ *
+ * Can be placed independently.
+ *
+ * Example:
+ *     val state = rememberScrollState(0f)
+ *
+ *     Box(Modifier.fillMaxSize()) {
+ *         ScrollableRow(state = state) {
+ *             ...
+ *         }
+ *
+ *         HorizontalScrollbar(
+ *             Modifier.align(Alignment.BottomCenter).fillMaxWidth(),
+ *             rememberScrollbarAdapter(state)
+ *         )
+ *     }
+ *
+ * @param adapter [ScrollbarAdapter] that will be used to communicate with scrollable component
+ * @param modifier the modifier to apply to this layout
+ * @param style [ScrollbarStyle] to define visual style of scrollbar
+ * @param interactionState [InteractionState] that will be updated when the element with this
+ * state is being dragged, using [Interaction.Dragged]
+ */
+@Composable
+fun HorizontalScrollbar(
+    adapter: ScrollbarAdapter,
+    modifier: Modifier = Modifier,
+    style: ScrollbarStyle = ScrollbarStyleAmbient.current,
+    interactionState: InteractionState = remember { InteractionState() }
+) = Scrollbar(
+    adapter,
+    modifier,
+    style,
+    interactionState,
+    isVertical = false
+)
+
+// TODO(demin): do we need to stop dragging if cursor is beyond constraints?
+// TODO(demin): add Interaction.Hovered to interactionState
+@OptIn(ExperimentalLayoutNodeApi::class)
+@Composable
+private fun Scrollbar(
+    adapter: ScrollbarAdapter,
+    modifier: Modifier = Modifier,
+    style: ScrollbarStyle,
+    interactionState: InteractionState,
+    isVertical: Boolean
+) = with(DensityAmbient.current) {
+    onDispose {
+        interactionState.removeInteraction(Interaction.Dragged)
+    }
+
+    var containerSize by remember { mutableStateOf(0) }
+    var isHover by remember { mutableStateOf(false) }
+
+    val minimalHeight = style.minimalHeight.toPx()
+    val sliderAdapter = remember(adapter, containerSize, minimalHeight) {
+        SliderAdapter(adapter, containerSize, minimalHeight)
+    }
+
+    val scrollThickness = style.thickness.toIntPx()
+    val measureBlocks = if (isVertical) {
+        remember(sliderAdapter, scrollThickness) {
+            verticalMeasureBlocks(sliderAdapter, { containerSize = it }, scrollThickness)
+        }
+    } else {
+        remember(sliderAdapter, scrollThickness) {
+            horizontalMeasureBlocks(sliderAdapter, { containerSize = it }, scrollThickness)
+        }
+    }
+
+    val dragObserver = object : DragObserver {
+        override fun onStart(downPosition: Offset) {
+            interactionState.addInteraction(Interaction.Dragged)
+        }
+
+        override fun onStop(velocity: Offset) {
+            interactionState.removeInteraction(Interaction.Dragged)
+        }
+
+        override fun onCancel() {
+            interactionState.removeInteraction(Interaction.Dragged)
+        }
+
+        override fun onDrag(dragDistance: Offset): Offset {
+            sliderAdapter.position += if (isVertical) dragDistance.y else dragDistance.x
+            return dragDistance
+        }
+    }
+
+    val color = animate(
+        if (isHover) style.hoverColor else style.unhoverColor,
+        animSpec = TweenSpec(durationMillis = style.hoverDurationMillis)
+    )
+
+    val isVisible = sliderAdapter.size < containerSize
+
+    Layout(
+        {
+            Box(
+                Modifier
+                    .background(if (isVisible) color else Color.Transparent, style.shape)
+                    .rawDragGestureFilter(dragObserver)
+            )
+        },
+        measureBlocks,
+        modifier
+            .pointerMoveFilter(
+                 isHover = false; true },
+                 isHover = true; true }
+            )
+            .scrollOnPressOutsideSlider(isVertical, sliderAdapter, adapter, containerSize)
+    )
+}
+
+private fun Modifier.scrollOnPressOutsideSlider(
+    isVertical: Boolean,
+    sliderAdapter: SliderAdapter,
+    scrollbarAdapter: ScrollbarAdapter,
+    containerSize: Int
+) = composed {
+    var targetOffset: Offset? by remember { mutableStateOf(null) }
+
+    if (targetOffset != null) {
+        val targetPosition = if (isVertical) targetOffset!!.y else targetOffset!!.x
+
+        LaunchedEffect(targetPosition) {
+            var delay = PressTimeout * 3
+            while (targetPosition !in sliderAdapter.bounds) {
+                val oldSign = sign(targetPosition - sliderAdapter.position)
+                scrollbarAdapter.scrollTo(
+                    containerSize,
+                    scrollbarAdapter.scrollOffset + oldSign * containerSize
+                )
+                val newSign = sign(targetPosition - sliderAdapter.position)
+
+                if (oldSign != newSign) {
+                    break
+                }
+
+                delay(delay.inMilliseconds())
+                delay = PressTimeout
+            }
+        }
+    }
+
+    pressIndicatorGestureFilter(
+         targetOffset = it },
+         targetOffset = null },
+         targetOffset = null }
+    )
+}
+
+/**
+ * Create and [remember] [ScrollbarAdapter] for scrollable container and current instance of
+ * [scrollState]
+ */
+@Composable
+fun rememberScrollbarAdapter(
+    scrollState: ScrollState
+): ScrollbarAdapter = remember(scrollState) {
+    ScrollbarAdapter(scrollState)
+}
+
+/**
+ * Create and [remember] [ScrollbarAdapter] for lazy scrollable container and current instance of
+ * [scrollState] and item configuration
+ */
+@ExperimentalFoundationApi
+@Composable
+fun rememberScrollbarAdapter(
+    scrollState: LazyListState,
+    itemCount: Int,
+    averageItemSize: Dp
+): ScrollbarAdapter {
+    val averageItemSizePx = with(DensityAmbient.current) {
+        averageItemSize.toPx()
+    }
+    return remember(scrollState, itemCount, averageItemSizePx) {
+        ScrollbarAdapter(scrollState, itemCount, averageItemSizePx)
+    }
+}
+
+/**
+ * ScrollbarAdapter for ScrollableColumn and ScrollableRow
+ *
+ * [scrollState] is instance of [ScrollState] which is used by scrollable component
+ *
+ * Example:
+ *     val state = rememberScrollState(0f)
+ *
+ *     Box(Modifier.fillMaxSize()) {
+ *         ScrollableColumn(state = state) {
+ *             ...
+ *         }
+ *
+ *         VerticalScrollbar(
+ *             Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
+ *             rememberScrollbarAdapter(state)
+ *         )
+ *     }
+ */
+fun ScrollbarAdapter(
+    scrollState: ScrollState
+): ScrollbarAdapter = ScrollableScrollbarAdapter(scrollState)
+
+private class ScrollableScrollbarAdapter(
+    private val scrollState: ScrollState
+) : ScrollbarAdapter {
+    override val scrollOffset: Float get() = scrollState.value
+
+    override suspend fun scrollTo(containerSize: Int, scrollOffset: Float) {
+        scrollState.scrollTo(scrollOffset)
+    }
+
+    override fun maxScrollOffset(containerSize: Int) =
+        scrollState.maxValue
+}
+
+// TODO(demin): if item height is different then slider will have wrong
+//  position when we dragging it (we can drag it to the beginning, but content will not be at the
+//  beginning). We can implement adaptive scrollbar height after b/170472532
+
+/**
+ * Experimental ScrollbarAdapter for lazy lists. Doesn't work stable with non-fixed item height.
+ *
+ * [scrollState] is instance of [LazyListState] which is used by scrollable component
+ *
+ * Scrollbar size and position will be calculated by passed [itemCount] and [averageItemSize]
+ *
+ * Example:
+ *     Box(Modifier.fillMaxSize()) {
+ *         val state = rememberLazyListState()
+ *         val itemCount = 100
+ *         val itemHeight = 20.dp
+ *
+ *         LazyColumn(state = state) {
+ *             ...
+ *         }
+ *
+ *         VerticalScrollbar(
+ *             Modifier.align(Alignment.CenterEnd),
+ *             rememberScrollbarAdapter(state, itemCount, itemHeight)
+ *         )
+ *     }
+ */
+@ExperimentalFoundationApi
+fun ScrollbarAdapter(
+    scrollState: LazyListState,
+    itemCount: Int,
+    averageItemSize: Float
+): ScrollbarAdapter = LazyScrollbarAdapter(
+    scrollState, itemCount, averageItemSize
+)
+
+private class LazyScrollbarAdapter(
+    private val scrollState: LazyListState,
+    private val itemCount: Int,
+    private val averageItemSize: Float
+) : ScrollbarAdapter {
+    override val scrollOffset: Float
+        get() = scrollState.firstVisibleItemIndex * averageItemSize +
+            scrollState.firstVisibleItemScrollOffset
+
+    override suspend fun scrollTo(containerSize: Int, scrollOffset: Float) {
+        val index = (scrollOffset / averageItemSize)
+            .toInt()
+            .coerceAtLeast(0)
+            .coerceAtMost(itemCount - 1)
+
+        scrollState.snapToItemIndex(
+            index = index,
+            scrollOffset = (scrollOffset - index * averageItemSize).toInt()
+        )
+    }
+
+    override fun maxScrollOffset(containerSize: Int) =
+        averageItemSize * itemCount - containerSize
+}
+
+/**
+ * Defines how to scroll the scrollable component
+ */
+interface ScrollbarAdapter {
+    /**
+     * Scroll offset of the content inside the scrollable component.
+     * Offset "100" means that the content is scrolled by 100 pixels from the start.
+     */
+    val scrollOffset: Float
+
+    /**
+     * Instantly jump to [scrollOffset] in pixels
+     *
+     * @param containerSize size of the scrollable container
+     *  (for example, it is height of ScrollableColumn if we use VerticalScrollbar)
+     * @param scrollOffset target value in pixels to jump to,
+     *  value will be coerced to 0..maxScrollOffset
+     */
+    suspend fun scrollTo(containerSize: Int, scrollOffset: Float)
+
+    /**
+     * Maximum scroll offset of the content inside the scrollable component
+     *
+     * @param containerSize size of the scrollable component
+     *  (for example, it is height of ScrollableColumn if we use VerticalScrollbar)
+     */
+    fun maxScrollOffset(containerSize: Int): Float
+}
+
+private class SliderAdapter(
+    val adapter: ScrollbarAdapter,
+    val containerSize: Int,
+    val minHeight: Float
+) {
+    private val contentSize = adapter.maxScrollOffset(containerSize) + containerSize
+    private val visiblePart get() = containerSize.toFloat() / contentSize
+
+    val size
+        get() = (containerSize * visiblePart)
+            .coerceAtLeast(minHeight)
+            .coerceAtMost(containerSize.toFloat())
+
+    private val scrollScale get() = (containerSize - size) / (contentSize - containerSize)
+
+    var position: Float
+        get() = scrollScale * adapter.scrollOffset
+        set(value) {
+            runBlocking {
+                adapter.scrollTo(containerSize, value / scrollScale)
+            }
+        }
+
+    val bounds get() = position..position + size
+}
+
+private fun verticalMeasureBlocks(
+    sliderAdapter: SliderAdapter,
+    setContainerSize: (Int) -> Unit,
+    scrollThickness: Int
+) = MeasuringIntrinsicsMeasureBlocks { measurables, constraints ->
+    setContainerSize(constraints.maxHeight)
+    val height = sliderAdapter.size.toInt()
+    val placeable = measurables.first().measure(
+        Constraints.fixed(
+            constraints.constrainWidth(scrollThickness),
+            height
+        )
+    )
+    layout(placeable.width, constraints.maxHeight) {
+        placeable.place(0, sliderAdapter.position.toInt())
+    }
+}
+
+private fun horizontalMeasureBlocks(
+    sliderAdapter: SliderAdapter,
+    setContainerSize: (Int) -> Unit,
+    scrollThickness: Int
+) = MeasuringIntrinsicsMeasureBlocks { measurables, constraints ->
+    setContainerSize(constraints.maxWidth)
+    val width = sliderAdapter.size.toInt()
+    val placeable = measurables.first().measure(
+        Constraints.fixed(
+            width,
+            constraints.constrainHeight(scrollThickness)
+        )
+    )
+    layout(constraints.maxWidth, placeable.height) {
+        placeable.place(sliderAdapter.position.toInt(), 0)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
new file mode 100644
index 0000000..4f02d06
--- /dev/null
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.input.mouse.MouseScrollEvent
+import androidx.compose.ui.input.mouse.MouseScrollUnit
+import androidx.compose.ui.platform.DesktopPlatform
+import androidx.compose.ui.platform.DesktopPlatformAmbient
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.down
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.junit4.DesktopComposeTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performGesture
+import androidx.compose.ui.test.swipe
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.milliseconds
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.jetbrains.skija.Surface
+import org.junit.Assert.assertEquals
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Suppress("WrapUnaryOperator")
+@OptIn(ExperimentalTesting::class)
+class ScrollbarTest {
+    @get:Rule
+    val rule = createComposeRule()
+    private val canvas = Surface.makeRasterN32Premul(100, 100).canvas
+
+    @Test
+    fun `drag slider to the middle`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 25f), end = Offset(0f, 50f))
+            }
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-50.dp)
+        }
+    }
+
+    @Test
+    fun `drag slider to the edges`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 25f), end = Offset(0f, 500f))
+            }
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 99f), end = Offset(0f, -500f))
+            }
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
+        }
+    }
+
+    @Test
+    fun `drag outside slider`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(10f, 25f), end = Offset(0f, 50f))
+            }
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
+        }
+    }
+
+    // TODO(demin): enable after we resolve b/171889442
+    @Ignore("Enable after we resolve b/171889442")
+    @Test
+    fun `mouseScroll over slider`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.performMouseScroll(0, 25, 1f)
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-10.dp)
+        }
+    }
+
+    // TODO(demin): enable after we resolve b/171889442
+    @Ignore("Enable after we resolve b/171889442")
+    @Test
+    fun `mouseScroll over scrollbar outside slider`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.performMouseScroll(0, 99, 1f)
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-10.dp)
+        }
+    }
+
+    // TODO(demin): enable after we resolve b/171889442
+    @Ignore("Enable after we resolve b/171889442")
+    @Test
+    fun `vertical mouseScroll over horizontal scrollbar `() {
+        runBlocking(Dispatchers.Main) {
+            // TODO(demin): write tests for vertical mouse scrolling over
+            //  horizontalScrollbar for the case when we have two-way scrollable content:
+            //  Modifier.verticalScrollbar(...).horizontalScrollbar(...)
+            //  Content should scroll vertically.
+        }
+    }
+
+    @Test
+    fun `mouseScroll over column then drag to the beginning`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 10, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.performMouseScroll(20, 25, 10f)
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 99f), end = Offset(0f, -500f))
+            }
+            onFrame()
+            rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
+        }
+    }
+
+    @Test(timeout = 3000)
+    fun `press on scrollbar outside slider`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 20, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                down(Offset(0f, 26f))
+            }
+
+            tryUntilSucceeded {
+                onFrame()
+                rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
+            }
+        }
+    }
+
+    @Test(timeout = 3000)
+    fun `press on the end of scrollbar outside slider`() {
+        runBlocking(Dispatchers.Main) {
+            rule.setContent {
+                TestBox(size = 100.dp, childSize = 20.dp, childCount = 20, scrollbarWidth = 10.dp)
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                down(Offset(0f, 99f))
+            }
+
+            tryUntilSucceeded {
+                onFrame()
+                rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-300.dp)
+            }
+        }
+    }
+
+    @Suppress("SameParameterValue")
+    @OptIn(ExperimentalFoundationApi::class)
+    @Test(timeout = 3000)
+    fun `scroll by less than one page in lazy list`() {
+        runBlocking(Dispatchers.Main) {
+            lateinit var state: LazyListState
+
+            rule.setContent {
+                state = rememberLazyListState()
+                LazyTestBox(
+                    state,
+                    size = 100.dp,
+                    childSize = 20.dp,
+                    childCount = 20,
+                    scrollbarWidth = 10.dp
+                )
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 0f), end = Offset(0f, 11f), duration = 1.milliseconds)
+            }
+            onFrame()
+            assertEquals(2, state.firstVisibleItemIndex)
+            assertEquals(4, state.firstVisibleItemScrollOffset)
+        }
+    }
+
+    @Suppress("SameParameterValue")
+    @OptIn(ExperimentalFoundationApi::class)
+    @Test(timeout = 3000)
+    fun `scroll by more than one page in lazy list`() {
+        runBlocking(Dispatchers.Main) {
+            lateinit var state: LazyListState
+
+            rule.setContent {
+                state = rememberLazyListState()
+                LazyTestBox(
+                    state,
+                    size = 100.dp,
+                    childSize = 20.dp,
+                    childCount = 20,
+                    scrollbarWidth = 10.dp
+                )
+            }
+            rule.awaitIdle()
+
+            rule.onNodeWithTag("scrollbar").performGesture {
+                swipe(start = Offset(0f, 0f), end = Offset(0f, 26f), duration = 1.milliseconds)
+            }
+            onFrame()
+            assertEquals(5, state.firstVisibleItemIndex)
+            assertEquals(4, state.firstVisibleItemScrollOffset)
+        }
+    }
+
+    private suspend fun tryUntilSucceeded(block: suspend () -> Unit) {
+        while (true) {
+            try {
+                block()
+                break
+            } catch (e: Throwable) {
+                delay(10)
+            }
+        }
+    }
+
+    // TODO(demin): move to DesktopComposeTestRule?
+    private suspend fun onFrame() {
+        (rule as DesktopComposeTestRule).owners?.onFrame(canvas, 100, 100, 0)
+        rule.awaitIdle()
+    }
+
+    private fun ComposeTestRule.performMouseScroll(x: Int, y: Int, delta: Float) {
+        (this as DesktopComposeTestRule).owners!!.onMouseScroll(
+            x, y, MouseScrollEvent(MouseScrollUnit.Line(delta), Orientation.Vertical)
+        )
+    }
+
+    @Composable
+    private fun TestBox(
+        size: Dp,
+        childSize: Dp,
+        childCount: Int,
+        scrollbarWidth: Dp,
+    ) = withTestEnvironment {
+        Box(Modifier.size(size)) {
+            val state = rememberScrollState()
+
+            ScrollableColumn(
+                Modifier.fillMaxSize().testTag("column"),
+                state
+            ) {
+                repeat(childCount) {
+                    Box(Modifier.size(childSize).testTag("box$it"))
+                }
+            }
+
+            VerticalScrollbar(
+                adapter = rememberScrollbarAdapter(state),
+                modifier = Modifier
+                    .width(scrollbarWidth)
+                    .fillMaxHeight()
+                    .testTag("scrollbar")
+            )
+        }
+    }
+
+    @Suppress("SameParameterValue")
+    @OptIn(ExperimentalFoundationApi::class)
+    @Composable
+    private fun LazyTestBox(
+        state: LazyListState,
+        size: Dp,
+        childSize: Dp,
+        childCount: Int,
+        scrollbarWidth: Dp,
+    ) = withTestEnvironment {
+        Box(Modifier.size(size)) {
+            LazyColumnFor(
+                (0 until childCount).toList(),
+                Modifier.fillMaxSize().testTag("column"),
+                state
+            ) {
+                Box(Modifier.size(childSize).testTag("box$it"))
+            }
+
+            VerticalScrollbar(
+                adapter = rememberScrollbarAdapter(state, childCount, childSize),
+                modifier = Modifier
+                    .width(scrollbarWidth)
+                    .fillMaxHeight()
+                    .testTag("scrollbar")
+            )
+        }
+    }
+
+    @Composable
+    private fun withTestEnvironment(content: @Composable () -> Unit) = Providers(
+        ScrollbarStyleAmbient provides ScrollbarStyle(
+            minimalHeight = 16.dp,
+            thickness = 8.dp,
+            shape = RectangleShape,
+            hoverDurationMillis = 300,
+            unhoverColor = Color.Black,
+            hoverColor = Color.Red
+        ),
+        DesktopPlatformAmbient provides DesktopPlatform.MacOS,
+        children = content
+    )
+}
\ No newline at end of file
diff --git a/compose/integration-tests/benchmark/build.gradle b/compose/integration-tests/benchmark/build.gradle
index f46802f..f9efbbfe2 100644
--- a/compose/integration-tests/benchmark/build.gradle
+++ b/compose/integration-tests/benchmark/build.gradle
@@ -33,6 +33,7 @@
     kotlinPlugin project(":compose:compiler:compiler")
 
     implementation project(":benchmark:benchmark-junit4")
+    implementation project(":benchmark:benchmark-perfetto")
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:integration-tests")
     implementation project(":compose:runtime:runtime")
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 64f3096..bfe8f59 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
@@ -35,7 +35,7 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4::class)
-class VectorBenchmark {
+open class VectorBenchmark {
     @get:Rule
     val benchmarkRule = ComposeBenchmarkRule()
 
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt
new file mode 100644
index 0000000..d4e1688
--- /dev/null
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.benchmark.test
+
+import androidx.benchmark.perfetto.PerfettoRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+/**
+ * Duplicate of [VectorBenchmark], but which adds tracing.
+ *
+ * Note: Per PerfettoRule, these benchmarks will be ignored < API 29
+ */
+@Suppress("ClassName")
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class VectorBenchmarkWithTracing : VectorBenchmark() {
+    @get:Rule
+    val perfettoRule = PerfettoRule()
+}
diff --git a/compose/integration-tests/demos/lint-baseline.xml b/compose/integration-tests/demos/lint-baseline.xml
deleted file mode 100644
index da9ae23..0000000
--- a/compose/integration-tests/demos/lint-baseline.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha06" client="gradle" variant="debug" version="4.2.0-alpha06">
-
-    <issue
-        id="UnusedResources"
-        message="The resource `R.mipmap.ic_launcher_round` appears to be unused"
-        errorLine1="&lt;adaptive-icon xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
-        errorLine2="^">
-        <location
-            file="src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml"
-            line="2"
-            column="1"/>
-        <location
-            file="src/main/res/mipmap-hdpi/ic_launcher_round.png"/>
-        <location
-            file="src/main/res/mipmap-mdpi/ic_launcher_round.png"/>
-        <location
-            file="src/main/res/mipmap-xhdpi/ic_launcher_round.png"/>
-        <location
-            file="src/main/res/mipmap-xxhdpi/ic_launcher_round.png"/>
-        <location
-            file="src/main/res/mipmap-xxxhdpi/ic_launcher_round.png"/>
-    </issue>
-
-    <issue
-        id="UnusedResources"
-        message="The resource `R.drawable.launch_background` appears to be unused"
-        errorLine1="&lt;layer-list xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
-        errorLine2="^">
-        <location
-            file="src/main/res/drawable/launch_background.xml"
-            line="3"
-            column="1"/>
-    </issue>
-
-    <issue
-        id="UnusedResources"
-        message="The resource `R.drawable.test` appears to be unused">
-        <location
-            file="src/main/res/drawable-nodpi/test.png"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `DemoTheme` of class `DemoActivityKt` requires synthetic accessor"
-        errorLine1="            DemoTheme(demoColors, window) {"
-        errorLine2="            ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoActivity.kt"
-            line="78"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `popBackStack` of class `Navigator` requires synthetic accessor"
-        errorLine1="            popBackStack()"
-        errorLine2="            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoActivity.kt"
-            line="153"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `generateRandomPalette` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="                    generateRandomPalette().saveColors(context)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="62"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `generateRandomPalette` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="                    generateRandomPalette().saveColors(context)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="62"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `saveColors` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="                    generateRandomPalette().saveColors(context)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="62"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `saveColors` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="                    generateRandomPalette().saveColors(context)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="62"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `forEachColorProperty` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="            lightColors().forEachColorProperty { name, color ->"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="83"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="SyntheticAccessor"
-        message="Access to `private` method `forEachColorProperty` of class `DemoSettingsActivityKt` requires synthetic accessor"
-        errorLine1="            darkColors().forEachColorProperty { name, color ->"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt"
-            line="98"
-            column="13"/>
-    </issue>
-
-</issues>
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 ad8bdbf..958458b 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
@@ -62,9 +62,7 @@
             FilteredDemoListItem(
                 demo,
                 filterText = filterText,
-                >
-                    onNavigate(it)
-                }
+                >
             )
         }
     }
diff --git a/compose/integration-tests/demos/src/main/res/drawable-nodpi/test.png b/compose/integration-tests/demos/src/main/res/drawable-nodpi/test.png
deleted file mode 100644
index 7cb7846..0000000
--- a/compose/integration-tests/demos/src/main/res/drawable-nodpi/test.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/demos/src/main/res/drawable/launch_background.xml b/compose/integration-tests/demos/src/main/res/drawable/launch_background.xml
deleted file mode 100644
index 304732f..0000000
--- a/compose/integration-tests/demos/src/main/res/drawable/launch_background.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Modify this file to customize your launch splash screen -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/white" />
-
-    <!-- You can insert your own image assets here -->
-    <!-- <item>
-        <bitmap
-            android:gravity="center"
-            android:src="@mipmap/launch_image" />
-    </item> -->
-</layer-list>
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/compose/integration-tests/demos/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index bbd3e02..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background"/>
-    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
-</adaptive-icon>
\ No newline at end of file
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher_round.png b/compose/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index dffca36..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher_round.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher_round.png b/compose/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index dae5e08..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher_round.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/compose/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 14ed0af..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher_round.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/compose/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index d8ae031..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/compose/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index beed3cd..0000000
--- a/compose/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
+++ /dev/null
Binary files differ
diff --git a/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt b/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
index 6be43cc..ba9d1c5 100644
--- a/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
+++ b/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
@@ -21,11 +21,12 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -63,8 +64,9 @@
             }
         }
 
-        val xmlBitmap = rule.onNodeWithTag(xmlTestCase.testTag).captureToBitmap()
-        val programmaticBitmap = rule.onNodeWithTag(programmaticTestCase.testTag).captureToBitmap()
+        val xmlBitmap = rule.onNodeWithTag(xmlTestCase.testTag).captureToImage().asAndroidBitmap()
+        val programmaticBitmap = rule.onNodeWithTag(programmaticTestCase.testTag).captureToImage()
+            .asAndroidBitmap()
 
         assertEquals(xmlBitmap.width, programmaticBitmap.width)
         assertEquals(xmlBitmap.height, programmaticBitmap.height)
@@ -97,7 +99,7 @@
             Image(vectorAsset, modifier = Modifier.testTag(testTag))
         }
 
-        rule.onNodeWithTag(testTag).captureToBitmap().apply {
+        rule.onNodeWithTag(testTag).captureToImage().asAndroidBitmap().apply {
             assertEquals(Color.Blue.toArgb(), getPixel(0, 0))
             assertEquals(Color.Blue.toArgb(), getPixel(width - 1, 0))
             assertEquals(Color.Blue.toArgb(), getPixel(0, height - 1))
diff --git a/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt b/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
index a640c27..89ef5a3 100644
--- a/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
+++ b/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
@@ -66,7 +66,6 @@
 import org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression
 import org.jetbrains.uast.visitor.AbstractUastVisitor
 
-private const val Modifier = "Modifier"
 private const val ModifierClass = "androidx.compose.ui.Modifier"
 private const val ModifierFile = "Modifier.kt"
 private const val ComposedModifierFile = "ComposedModifier.kt"
@@ -175,7 +174,7 @@
             (type as? PsiWildcardType)?.bound?.canonicalText
 
         private fun isThenFunctionCall(node: UQualifiedReferenceExpression): Boolean {
-            if (node.receiver !is UThisExpression) return false
+            if (!isModifierType(node.receiver.getExpressionType())) return false
             val then = node.selector as? KotlinUFunctionCallExpression ?: return false
             return then.methodName == ThenMethodName &&
                 then.valueArguments.size == 1 &&
@@ -442,6 +441,7 @@
                 node: UQualifiedReferenceExpression
             ): Boolean {
                 if (isThenFunctionCall(node)) {
+                    node.receiver.accept(this)
                     val then = node.selector as KotlinUFunctionCallExpression
                     then.valueArguments.first().accept(modifierVisitor)
                     return true
@@ -504,6 +504,11 @@
                     definition.accept(this)
                     return true
                 }
+                if (isModifierType(node.receiverType) && isModifierType(node.returnType)) {
+                    // For now accept all other calls. Assume that the method being called
+                    // will add inspector information.
+                    return true
+                }
                 return super.visitCallExpression(node)
             }
 
diff --git a/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt b/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
index 1d3bead..f4d7440 100644
--- a/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
+++ b/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
@@ -409,25 +409,31 @@
                 import androidx.compose.ui.platform.InspectorValueInfo
                 import androidx.compose.ui.platform.debugInspectorInfo
 
-                fun Modifier.border(painter: Painter) =
+                fun Modifier.padding(size: Int) =
                     this.then(
-                        if (painter.size > 0) {
-                            BorderModifier(inspectorInfo = debugInspectorInfo {
-                                name = "border"
-                                properties["painter"] = painter
-                            })
+                        if (size >= 10) {
+                            PaddingModifier(
+                                paddingSize = size,
+                                inspectorInfo = debugInspectorInfo {
+                                    name = "padding"
+                                    properties["size"] = size
+                                }
+                            )
                         } else {
                             Modifier
                         }
                     )
 
-                private class BorderModifier(
+                fun Modifier.paddingFromBaseline(top: Int, bottom: Int) = this
+                    .then(if (bottom > 0) padding(bottom) else Modifier)
+                    .then(if (top > 0) padding(top) else Modifier)
+
+                private class PaddingModifier(
+                    paddingSize: Int,
                     inspectorInfo: InspectorInfo.() -> Unit
                 ): Modifier.Element, InspectorValueInfo(inspectorInfo) {
                 }
 
-                class Painter(val size: Int)
-
                 """
             ).indented()
         )
diff --git a/compose/material/material-icons-extended/src/androidAndroidTest/kotlin/androidx/compose/material/icons/IconComparisonTest.kt b/compose/material/material-icons-extended/src/androidAndroidTest/kotlin/androidx/compose/material/icons/IconComparisonTest.kt
index 78d8e64..1d8f100 100644
--- a/compose/material/material-icons-extended/src/androidAndroidTest/kotlin/androidx/compose/material/icons/IconComparisonTest.kt
+++ b/compose/material/material-icons-extended/src/androidAndroidTest/kotlin/androidx/compose/material/icons/IconComparisonTest.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.draw.paint
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.vector.VectorAsset
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
 import androidx.compose.ui.platform.ContextAmbient
@@ -36,7 +37,7 @@
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.test.filters.LargeTest
@@ -122,8 +123,8 @@
             assertVectorAssetsAreEqual(xmlVector!!, programmaticVector, iconName)
 
             matcher.assertBitmapsAreEqual(
-                rule.onNodeWithTag(XmlTestTag).captureToBitmap(),
-                rule.onNodeWithTag(ProgrammaticTestTag).captureToBitmap(),
+                rule.onNodeWithTag(XmlTestTag).captureToImage().asAndroidBitmap(),
+                rule.onNodeWithTag(ProgrammaticTestTag).captureToImage().asAndroidBitmap(),
                 iconName
             )
 
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index e91c285..01a846f 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -223,6 +223,20 @@
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
 
+  public final class ContentAlpha {
+    method public float getDisabled();
+    method public float getHigh();
+    method public float getMedium();
+    property public final float disabled;
+    property public final float high;
+    property public final float medium;
+    field public static final androidx.compose.material.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableAmbient<java.lang.Float> getAmbientContentAlpha();
+  }
+
   public final class ContentColorKt {
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.graphics.Color> getAmbientContentColor();
   }
@@ -311,19 +325,19 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.material.ElevationOverlay> getAmbientElevationOverlay();
   }
 
-  @androidx.compose.runtime.Immutable public interface Emphasis {
-    method public long applyEmphasis-8_81llA(long color);
+  @Deprecated @androidx.compose.runtime.Immutable public interface Emphasis {
+    method @Deprecated public long applyEmphasis-8_81llA(long color);
   }
 
   public final class EmphasisKt {
-    method @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
+    method @Deprecated @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
   }
 
-  public interface EmphasisLevels {
-    method public androidx.compose.material.Emphasis getDisabled();
-    method public androidx.compose.material.Emphasis getHigh();
-    method public androidx.compose.material.Emphasis getMedium();
+  @Deprecated public interface EmphasisLevels {
+    method @Deprecated public androidx.compose.material.Emphasis getDisabled();
+    method @Deprecated public androidx.compose.material.Emphasis getHigh();
+    method @Deprecated public androidx.compose.material.Emphasis getMedium();
     property public abstract androidx.compose.material.Emphasis disabled;
     property public abstract androidx.compose.material.Emphasis high;
     property public abstract androidx.compose.material.Emphasis medium;
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index e91c285..01a846f 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -223,6 +223,20 @@
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
 
+  public final class ContentAlpha {
+    method public float getDisabled();
+    method public float getHigh();
+    method public float getMedium();
+    property public final float disabled;
+    property public final float high;
+    property public final float medium;
+    field public static final androidx.compose.material.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableAmbient<java.lang.Float> getAmbientContentAlpha();
+  }
+
   public final class ContentColorKt {
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.graphics.Color> getAmbientContentColor();
   }
@@ -311,19 +325,19 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.material.ElevationOverlay> getAmbientElevationOverlay();
   }
 
-  @androidx.compose.runtime.Immutable public interface Emphasis {
-    method public long applyEmphasis-8_81llA(long color);
+  @Deprecated @androidx.compose.runtime.Immutable public interface Emphasis {
+    method @Deprecated public long applyEmphasis-8_81llA(long color);
   }
 
   public final class EmphasisKt {
-    method @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
+    method @Deprecated @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
   }
 
-  public interface EmphasisLevels {
-    method public androidx.compose.material.Emphasis getDisabled();
-    method public androidx.compose.material.Emphasis getHigh();
-    method public androidx.compose.material.Emphasis getMedium();
+  @Deprecated public interface EmphasisLevels {
+    method @Deprecated public androidx.compose.material.Emphasis getDisabled();
+    method @Deprecated public androidx.compose.material.Emphasis getHigh();
+    method @Deprecated public androidx.compose.material.Emphasis getMedium();
     property public abstract androidx.compose.material.Emphasis disabled;
     property public abstract androidx.compose.material.Emphasis high;
     property public abstract androidx.compose.material.Emphasis medium;
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index e91c285..01a846f 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -223,6 +223,20 @@
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
 
+  public final class ContentAlpha {
+    method public float getDisabled();
+    method public float getHigh();
+    method public float getMedium();
+    property public final float disabled;
+    property public final float high;
+    property public final float medium;
+    field public static final androidx.compose.material.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableAmbient<java.lang.Float> getAmbientContentAlpha();
+  }
+
   public final class ContentColorKt {
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.graphics.Color> getAmbientContentColor();
   }
@@ -311,19 +325,19 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.material.ElevationOverlay> getAmbientElevationOverlay();
   }
 
-  @androidx.compose.runtime.Immutable public interface Emphasis {
-    method public long applyEmphasis-8_81llA(long color);
+  @Deprecated @androidx.compose.runtime.Immutable public interface Emphasis {
+    method @Deprecated public long applyEmphasis-8_81llA(long color);
   }
 
   public final class EmphasisKt {
-    method @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
+    method @Deprecated @androidx.compose.runtime.Composable public static void ProvideEmphasis(androidx.compose.material.Emphasis emphasis, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Ambient<androidx.compose.material.EmphasisLevels> getAmbientEmphasisLevels();
   }
 
-  public interface EmphasisLevels {
-    method public androidx.compose.material.Emphasis getDisabled();
-    method public androidx.compose.material.Emphasis getHigh();
-    method public androidx.compose.material.Emphasis getMedium();
+  @Deprecated public interface EmphasisLevels {
+    method @Deprecated public androidx.compose.material.Emphasis getDisabled();
+    method @Deprecated public androidx.compose.material.Emphasis getHigh();
+    method @Deprecated public androidx.compose.material.Emphasis getMedium();
     property public abstract androidx.compose.material.Emphasis disabled;
     property public abstract androidx.compose.material.Emphasis high;
     property public abstract androidx.compose.material.Emphasis medium;
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index 84baf44..5077db4 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -58,6 +58,7 @@
         testImplementation(TRUTH)
 
         androidTestImplementation project(":compose:material:material:material-samples")
+        androidTestImplementation project(":compose:test-utils")
         androidTestImplementation project(":test-screenshot")
         androidTestImplementation project(":ui:ui-test")
         androidTestImplementation(ANDROIDX_TEST_RULES)
@@ -114,6 +115,7 @@
 
             androidAndroidTest.dependencies {
                 implementation project(":compose:material:material:material-samples")
+                implementation project(":compose:test-utils")
                 implementation project(":test-screenshot")
                 implementation project(":ui:ui-test")
 
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
index c5f7dde..038d79e 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
@@ -23,10 +23,10 @@
 import androidx.compose.material.samples.BackdropScaffoldSample
 import androidx.compose.material.samples.BottomDrawerSample
 import androidx.compose.material.samples.CustomAlertDialogSample
-import androidx.compose.material.samples.EmphasisSample
 import androidx.compose.material.samples.ModalBottomSheetSample
 import androidx.compose.material.samples.ModalDrawerSample
 import androidx.compose.material.samples.BottomSheetScaffoldSample
+import androidx.compose.material.samples.ContentAlphaSample
 import androidx.compose.material.samples.ScaffoldWithBottomBarAndCutout
 import androidx.compose.material.samples.ScaffoldWithCoroutinesSnackbar
 import androidx.compose.material.samples.ScaffoldWithSimpleSnackbar
@@ -55,7 +55,7 @@
             )
         ),
         ComposableDemo("Elevation") { ElevationDemo() },
-        ComposableDemo("Emphasis") { EmphasisSample() },
+        ComposableDemo("Content alpha") { ContentAlphaSample() },
         DemoCategory(
             "ListItems",
             listOf(
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 e43a686..b41684f 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
@@ -29,8 +29,8 @@
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.selection.selectable
-import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Checkbox
+import androidx.compose.material.ContentAlpha
 import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
@@ -218,7 +218,7 @@
     val typography = MaterialTheme.typography.caption
     val color = when (helperMessageOption) {
         Option.Helper -> {
-            AmbientEmphasisLevels.current.medium.applyEmphasis(MaterialTheme.colors.onSurface)
+            MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)
         }
         Option.Error -> MaterialTheme.colors.error
         else -> Color.Unspecified
@@ -228,7 +228,8 @@
         Box(modifier = Modifier.weight(1f, fill = false)) { textField() }
         Text(
             text = "Helper message",
-            style = typography.copy(color = color),
+            color = color,
+            style = typography,
             modifier = Modifier.padding(start = 16.dp)
         )
     }
diff --git a/compose/material/material/lint-baseline.xml b/compose/material/material/lint-baseline.xml
deleted file mode 100644
index 7d9e2d4..0000000
--- a/compose/material/material/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha06" client="gradle" variant="debug" version="4.2.0-alpha06">
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1=") = composed {"
-        errorLine2="    ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"
-            line="437"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="    ): Modifier = composed {"
-        errorLine2="                  ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material/Tab.kt"
-            line="221"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="    this.then(object : LayoutModifier {"
-        errorLine2="              ^">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt"
-            line="383"
-            column="15"/>
-    </issue>
-
-</issues>
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ContentAlphaSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ContentAlphaSamples.kt
new file mode 100644
index 0000000..2ca30b2
--- /dev/null
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ContentAlphaSamples.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.AmbientContentAlpha
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
+
+@Sampled
+@Composable
+fun ContentAlphaSample() {
+    Column {
+        Text("No alpha applied - 100% opacity")
+        Providers(AmbientContentAlpha provides ContentAlpha.high) {
+            Text("High content alpha applied - 87% opacity")
+        }
+        Providers(AmbientContentAlpha provides ContentAlpha.medium) {
+            Text("Medium content alpha applied - 60% opacity")
+        }
+        Providers(AmbientContentAlpha provides ContentAlpha.disabled) {
+            Text("Disabled content alpha applied - 38% opacity")
+        }
+    }
+}
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/EmphasisSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/EmphasisSamples.kt
deleted file mode 100644
index 07c2414..0000000
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/EmphasisSamples.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material.AmbientEmphasisLevels
-import androidx.compose.material.ProvideEmphasis
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-
-@Sampled
-@Composable
-fun EmphasisSample() {
-    Column {
-        Text("No emphasis applied - 100% opacity")
-        val emphasisLevels = AmbientEmphasisLevels.current
-        ProvideEmphasis(emphasisLevels.high) {
-            Text("High emphasis applied - 87% opacity")
-        }
-        ProvideEmphasis(emphasisLevels.medium) {
-            Text("Medium emphasis applied - 60% opacity")
-        }
-        ProvideEmphasis(emphasisLevels.disabled) {
-            Text("Disabled emphasis applied - 38% opacity")
-        }
-    }
-}
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 7005f37..cd11a83 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
@@ -20,7 +20,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.ContentAlpha
 import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
@@ -128,7 +128,7 @@
         val textColor = if (invalidInput) {
             MaterialTheme.colors.error
         } else {
-            AmbientEmphasisLevels.current.medium.applyEmphasis(MaterialTheme.colors.onSurface)
+            MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)
         }
         Text(
             text = if (invalidInput) "Requires '@' and at least 5 symbols" else "Helper message",
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogScreenshotTest.kt
index ed8e5e4..328c77b8 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogScreenshotTest.kt
@@ -17,14 +17,14 @@
 package androidx.compose.material
 
 import android.os.Build
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.isDialog
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,7 +61,7 @@
         }
 
         rule.onNode(isDialog())
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, "dialog_sideBySideButtons")
     }
 
@@ -86,7 +86,7 @@
         }
 
         rule.onNode(isDialog())
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, "dialog_stackedButtons")
     }
 }
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
index de4fd9f..375d93d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AlertDialogTest.kt
@@ -18,10 +18,10 @@
 
 import android.os.Build
 import androidx.compose.foundation.border
+import androidx.compose.testutils.assertContainsColor
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.test.assertContainsColor
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.isDialog
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.dp
@@ -62,7 +62,7 @@
 
         // Assert background
         rule.onNode(isDialog())
-            .captureToBitmap()
+            .captureToImage()
             .assertContainsColor(Color.Yellow) // Background
             .assertContainsColor(Color.Blue) // Modifier border
 
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 f6aa0e6..d468b4b 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
@@ -23,19 +23,19 @@
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -296,7 +296,7 @@
 
         // Capture and compare screenshots
         composeTestRule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
@@ -354,8 +354,7 @@
 ) {
     // Apply default emphasis
     @Suppress("NAME_SHADOWING")
-    val unselectedContentColor = AmbientEmphasisLevels.current.medium
-        .applyEmphasis(unselectedContentColor)
+    val unselectedContentColor = unselectedContentColor.copy(alpha = ContentAlpha.medium)
     Box(Modifier.semantics(mergeAllDescendants = true) {}.testTag(Tag)) {
         BottomNavigation(backgroundColor = backgroundColor) {
             BottomNavigationItem(
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 87f3000..e69a47f 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
@@ -32,13 +32,14 @@
 import androidx.compose.ui.draw.drawShadow
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInParent
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performGesture
@@ -381,7 +382,7 @@
         }
 
         rule.onNodeWithTag("Scaffold")
-            .captureToBitmap().apply {
+            .captureToImage().asAndroidBitmap().apply {
                 // asserts the appbar(top half part) has the shadow
                 val yPos = height / 2 + 2
                 Truth.assertThat(Color(getPixel(0, yPos))).isNotEqualTo(Color.White)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
index c6bbe0d..632ae05 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
@@ -19,8 +19,9 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.hasClickAction
@@ -33,7 +34,6 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,7 +58,7 @@
         }
 
         rule.onNode(hasClickAction())
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, "button_default")
     }
 
@@ -71,7 +71,7 @@
         }
 
         rule.onNodeWithText("Button")
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, "button_disabled")
     }
 
@@ -94,7 +94,7 @@
         rule.clockTestRule.advanceClock(50)
 
         rule.onRoot()
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, "button_ripple")
     }
 }
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
index b43d4a2..03c84ef 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -51,9 +52,8 @@
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotEnabled
-import androidx.compose.ui.test.assertShape
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.hasClickAction
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -294,7 +294,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = shape,
@@ -369,7 +369,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 horizontalPadding = padding,
@@ -405,7 +405,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 horizontalPadding = padding,
@@ -436,7 +436,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -464,7 +464,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -477,48 +477,48 @@
     fun containedButtonDisabledContentColorIsCorrect() {
         var >
         var content = Color.Transparent
-        var emphasis: Emphasis? = null
+        var disabledAlpha = 1f
         rule.setMaterialContent {
             >
-            emphasis = AmbientEmphasisLevels.current.disabled
+            disabledAlpha = ContentAlpha.disabled
             Button( enabled = false) {
-                content = AmbientContentColor.current
+                content = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
             }
         }
 
-        assertThat(content).isEqualTo(emphasis!!.applyEmphasis(onSurface))
+        assertThat(content).isEqualTo(onSurface.copy(alpha = disabledAlpha))
     }
 
     @Test
     fun outlinedButtonDisabledContentColorIsCorrect() {
         var >
         var content = Color.Transparent
-        var emphasis: Emphasis? = null
+        var disabledAlpha = 1f
         rule.setMaterialContent {
             >
-            emphasis = AmbientEmphasisLevels.current.disabled
+            disabledAlpha = ContentAlpha.disabled
             OutlinedButton( enabled = false) {
-                content = AmbientContentColor.current
+                content = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
             }
         }
 
-        assertThat(content).isEqualTo(emphasis!!.applyEmphasis(onSurface))
+        assertThat(content).isEqualTo(onSurface.copy(alpha = disabledAlpha))
     }
 
     @Test
     fun textButtonDisabledContentColorIsCorrect() {
         var >
         var content = Color.Transparent
-        var emphasis: Emphasis? = null
+        var disabledAlpha = 1f
         rule.setMaterialContent {
             >
-            emphasis = AmbientEmphasisLevels.current.disabled
+            disabledAlpha = ContentAlpha.disabled
             TextButton( enabled = false) {
-                content = AmbientContentColor.current
+                content = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
             }
         }
 
-        assertThat(content).isEqualTo(emphasis!!.applyEmphasis(onSurface))
+        assertThat(content).isEqualTo(onSurface.copy(alpha = disabledAlpha))
     }
 
     @Test
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CardTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CardTest.kt
index b54de36..41930d9 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CardTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CardTest.kt
@@ -21,12 +21,12 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.dp
@@ -71,7 +71,7 @@
         }
 
         rule.onNodeWithTag("card")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = shape,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
index 72db267..b2ccc55 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
@@ -21,10 +21,11 @@
 import androidx.compose.foundation.selection.ToggleableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.isToggleable
@@ -37,7 +38,6 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -198,7 +198,7 @@
     private fun assertToggeableAgainstGolden(goldenName: String) {
         // TODO: replace with find(isToggeable()) after b/157687898 is fixed
         rule.onNodeWithTag(wrapperTestTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenName)
     }
 }
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ContentAlphaTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ContentAlphaTest.kt
new file mode 100644
index 0000000..e6fbd29
--- /dev/null
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ContentAlphaTest.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@Suppress("unused")
+@MediumTest
+@RunWith(Parameterized::class)
+class ContentAlphaTest(private val colors: Colors, private val debugParameterName: String) {
+    private val ReducedContrastHighContentAlpha = 0.87f
+    private val ReducedContrastMediumContentAlpha = 0.60f
+    private val ReducedContrastDisabledContentAlpha = 0.38f
+
+    private val HighContrastHighContentAlpha = 1.00f
+    private val HighContrastMediumContentAlpha = 0.74f
+    private val HighContrastDisabledContentAlpha = 0.38f
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{1}")
+        fun initColors() = arrayOf(
+            arrayOf(lightColors(), "Light theme"),
+            arrayOf(darkColors(), "Dark theme")
+        )
+    }
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun noContentAlphaSpecified_contentColorUnmodified_surface() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface {
+                    val >
+
+                    assertThat(AmbientContentColor.current).isEqualTo(onSurface)
+                }
+            }
+        }
+    }
+
+    @Test
+    fun highContentAlpha_contentColorSet_surface() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface {
+                    Providers(AmbientContentAlpha provides ContentAlpha.high) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(ReducedContrastHighContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun mediumContentAlpha_contentColorSet_surface() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface {
+                    Providers(AmbientContentAlpha provides ContentAlpha.medium) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(ReducedContrastMediumContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun disabledContentAlpha_contentColorSet_surface() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface {
+                    Providers(AmbientContentAlpha provides ContentAlpha.disabled) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(ReducedContrastDisabledContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun noContentAlphaSpecified_contentColorUnmodified_primary() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface(color = colors.primary) {
+                    val >
+
+                    assertThat(AmbientContentColor.current).isEqualTo(onPrimary)
+                }
+            }
+        }
+    }
+
+    @Test
+    fun highContentAlpha_contentColorSet_primary() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface(color = colors.primary) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.high) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(HighContrastHighContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun mediumContentAlpha_contentColorSet_primary() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface(color = colors.primary) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.medium) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(HighContrastMediumContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun disabledContentAlpha_contentColorSet_primary() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface(color = colors.primary) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.disabled) {
+                        assertThat(AmbientContentAlpha.current)
+                            .isEqualTo(HighContrastDisabledContentAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun noContentAlphaSpecified_contentColorUnmodified_colorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                Surface(contentColor = Color.Yellow) {
+                    assertThat(AmbientContentColor.current).isEqualTo(Color.Yellow)
+                }
+            }
+        }
+    }
+
+    @Test
+    fun highContentAlpha_contentColorSet_highLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.9f, 0.9f, 0.9f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.high) {
+                        val expectedAlpha = if (colors.isLight) {
+                            HighContrastHighContentAlpha
+                        } else {
+                            ReducedContrastHighContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun mediumContentAlpha_contentColorSet_highLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.9f, 0.9f, 0.9f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.medium) {
+                        val expectedAlpha = if (colors.isLight) {
+                            HighContrastMediumContentAlpha
+                        } else {
+                            ReducedContrastMediumContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun disabledContentAlpha_contentColorSet_highLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.9f, 0.9f, 0.9f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.disabled) {
+                        val expectedAlpha = if (colors.isLight) {
+                            HighContrastDisabledContentAlpha
+                        } else {
+                            ReducedContrastDisabledContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun highContentAlpha_contentColorSet_lowLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.1f, 0.1f, 0.1f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.high) {
+                        val expectedAlpha = if (colors.isLight) {
+                            ReducedContrastHighContentAlpha
+                        } else {
+                            HighContrastHighContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun mediumContentAlpha_contentColorSet_lowLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.1f, 0.1f, 0.1f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.medium) {
+                        val expectedAlpha = if (colors.isLight) {
+                            ReducedContrastMediumContentAlpha
+                        } else {
+                            HighContrastMediumContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun disabledContentAlpha_contentColorSet_lowLuminanceColorNotFromTheme() {
+        rule.setContent {
+            MaterialTheme(colors) {
+                val contentColor = Color(0.1f, 0.1f, 0.1f)
+                Surface(contentColor = contentColor) {
+                    Providers(AmbientContentAlpha provides ContentAlpha.disabled) {
+                        val expectedAlpha = if (colors.isLight) {
+                            ReducedContrastDisabledContentAlpha
+                        } else {
+                            HighContrastDisabledContentAlpha
+                        }
+                        assertThat(AmbientContentAlpha.current).isEqualTo(expectedAlpha)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerScreenshotTest.kt
index 28b8ddb..e30d3a0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerScreenshotTest.kt
@@ -21,10 +21,11 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.ComposeTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -33,7 +34,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -99,7 +99,7 @@
 
     private fun assertScreenshotAgainstGolden(goldenName: String) {
         rule.onNodeWithTag("container")
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenName)
     }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ElevationOverlayTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ElevationOverlayTest.kt
index 8591998..babd592 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ElevationOverlayTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ElevationOverlayTest.kt
@@ -21,13 +21,13 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
@@ -81,7 +81,7 @@
         )
 
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 expectedSurfaceColor
             }
@@ -103,7 +103,7 @@
             .compositeOver(colors.surface)
 
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 expectedSurfaceColor
             }
@@ -128,7 +128,7 @@
         )
 
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 expectedSurfaceColor
             }
@@ -146,7 +146,7 @@
         val expectedSurfaceColor = colors.surface
 
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 expectedSurfaceColor
             }
@@ -167,7 +167,7 @@
         val expectedSurfaceColor = colors.surface
 
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 expectedSurfaceColor
             }
@@ -190,7 +190,7 @@
 
         rule
             .onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(SurfaceSize) {
                 customOverlayColor
             }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/EmphasisTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/EmphasisTest.kt
deleted file mode 100644
index 15f3d39..0000000
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/EmphasisTest.kt
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material
-
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-@Suppress("unused")
-@MediumTest
-@RunWith(Parameterized::class)
-class EmphasisTest(private val colors: Colors, private val debugParameterName: String) {
-    private val ReducedContrastHighEmphasisAlpha = 0.87f
-    private val ReducedContrastMediumEmphasisAlpha = 0.60f
-    private val ReducedContrastDisabledEmphasisAlpha = 0.38f
-
-    private val HighContrastHighEmphasisAlpha = 1.00f
-    private val HighContrastMediumEmphasisAlpha = 0.74f
-    private val HighContrastDisabledEmphasisAlpha = 0.38f
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{1}")
-        fun initColors() = arrayOf(
-            arrayOf(lightColors(), "Light theme"),
-            arrayOf(darkColors(), "Dark theme")
-        )
-    }
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun noEmphasisSpecified_contentColorUnmodified_surface() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface {
-                    val >
-
-                    assertThat(AmbientContentColor.current).isEqualTo(onSurface)
-                }
-            }
-        }
-    }
-
-    @Test
-    fun highEmphasis_contentColorSet_surface() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.high) {
-                        val >
-                        val modifiedOnSurface = onSurface.copy(
-                            alpha = ReducedContrastHighEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnSurface)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun mediumEmphasis_contentColorSet_surface() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.medium) {
-                        val >
-                        val modifiedOnSurface = onSurface.copy(
-                            alpha = ReducedContrastMediumEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnSurface)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun disabledEmphasis_contentColorSet_surface() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.disabled) {
-                        val >
-                        val modifiedOnSurface = onSurface.copy(
-                            alpha = ReducedContrastDisabledEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnSurface)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun noEmphasisSpecified_contentColorUnmodified_primary() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface(color = colors.primary) {
-                    val >
-
-                    assertThat(AmbientContentColor.current).isEqualTo(onPrimary)
-                }
-            }
-        }
-    }
-
-    @Test
-    fun highEmphasis_contentColorSet_primary() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface(color = colors.primary) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.high) {
-                        val >
-                        val modifiedOnPrimary = onPrimary.copy(
-                            alpha = HighContrastHighEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnPrimary)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun mediumEmphasis_contentColorSet_primary() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface(color = colors.primary) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.medium) {
-                        val >
-                        val modifiedOnPrimary = onPrimary.copy(
-                            alpha = HighContrastMediumEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnPrimary)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun disabledEmphasis_contentColorSet_primary() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface(color = colors.primary) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.disabled) {
-                        val >
-                        val modifiedOnPrimary = onPrimary.copy(
-                            alpha = HighContrastDisabledEmphasisAlpha
-                        )
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedOnPrimary)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun noEmphasisSpecified_contentColorUnmodified_colorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                Surface(contentColor = Color.Yellow) {
-                    assertThat(AmbientContentColor.current).isEqualTo(Color.Yellow)
-                }
-            }
-        }
-    }
-
-    @Test
-    fun highEmphasis_contentColorSet_highLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.9f, 0.9f, 0.9f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.high) {
-                        val expectedAlpha = if (colors.isLight) {
-                            HighContrastHighEmphasisAlpha
-                        } else {
-                            ReducedContrastHighEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun mediumEmphasis_contentColorSet_highLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.9f, 0.9f, 0.9f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.medium) {
-                        val expectedAlpha = if (colors.isLight) {
-                            HighContrastMediumEmphasisAlpha
-                        } else {
-                            ReducedContrastMediumEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun disabledEmphasis_contentColorSet_highLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.9f, 0.9f, 0.9f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.disabled) {
-                        val expectedAlpha = if (colors.isLight) {
-                            HighContrastDisabledEmphasisAlpha
-                        } else {
-                            ReducedContrastDisabledEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun highEmphasis_contentColorSet_lowLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.1f, 0.1f, 0.1f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.high) {
-                        val expectedAlpha = if (colors.isLight) {
-                            ReducedContrastHighEmphasisAlpha
-                        } else {
-                            HighContrastHighEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun mediumEmphasis_contentColorSet_lowLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.1f, 0.1f, 0.1f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.medium) {
-                        val expectedAlpha = if (colors.isLight) {
-                            ReducedContrastMediumEmphasisAlpha
-                        } else {
-                            HighContrastMediumEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun disabledEmphasis_contentColorSet_lowLuminanceColorNotFromTheme() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.1f, 0.1f, 0.1f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.disabled) {
-                        val expectedAlpha = if (colors.isLight) {
-                            ReducedContrastDisabledEmphasisAlpha
-                        } else {
-                            HighContrastDisabledEmphasisAlpha
-                        }
-                        val modifiedColor = contentColor.copy(alpha = expectedAlpha)
-
-                        assertThat(AmbientContentColor.current).isEqualTo(modifiedColor)
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    fun translucentColor_emphasisNotApplied() {
-        rule.setContent {
-            MaterialTheme(colors) {
-                val contentColor = Color(0.5f, 0.5f, 0.5f, 0.5f)
-                Surface(contentColor = contentColor) {
-                    ProvideEmphasis(AmbientEmphasisLevels.current.high) {
-                        assertThat(AmbientContentColor.current).isEqualTo(contentColor)
-                    }
-                }
-            }
-        }
-    }
-}
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 cf9455f..f81b3d9 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
@@ -26,6 +26,7 @@
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
@@ -35,10 +36,9 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
-import androidx.compose.ui.test.assertShape
 import androidx.compose.ui.test.assertWidthIsAtLeast
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
@@ -193,7 +193,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = realShape,
@@ -229,7 +229,7 @@
         }
 
         rule.onNodeWithTag("myButton")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = realShape,
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 d775694..abb9316 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
@@ -17,6 +17,7 @@
 
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Menu
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
@@ -29,9 +30,8 @@
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHeightIsEqualTo
-import androidx.compose.ui.test.assertPixels
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -161,7 +161,7 @@
         }
 
         // With no color provided for a tint, the icon should render the original pixels
-        rule.onNodeWithTag(testTag).captureToBitmap().assertPixels { Color.Red }
+        rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Red }
     }
 
     @Test
@@ -183,7 +183,7 @@
         }
 
         // With a tint color provided, all pixels should be blue
-        rule.onNodeWithTag(testTag).captureToBitmap().assertPixels { Color.Blue }
+        rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
     }
 
     private fun createBitmapWithColor(
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
index 7ae1a013..a01aa52 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MenuTest.kt
@@ -257,13 +257,13 @@
         var >
         var enabledContentColor = Color.Unspecified
         var disabledContentColor = Color.Unspecified
-        lateinit var enabledEmphasis: Emphasis
-        lateinit var disabledEmphasis: Emphasis
+        var enabledContentAlpha = 1f
+        var disabledContentAlpha = 1f
 
         rule.setContent {
             >
-            enabledEmphasis = AmbientEmphasisLevels.current.high
-            disabledEmphasis = AmbientEmphasisLevels.current.disabled
+            enabledContentAlpha = ContentAlpha.high
+            disabledContentAlpha = ContentAlpha.disabled
             DropdownMenu(
                 toggle = { Box(Modifier.size(20.dp)) },
                 >
@@ -271,15 +271,17 @@
             ) {
                 DropdownMenuItem( {
                     enabledContentColor = AmbientContentColor.current
+                        .copy(alpha = AmbientContentAlpha.current)
                 }
                 DropdownMenuItem(enabled = false,  {
                     disabledContentColor = AmbientContentColor.current
+                        .copy(alpha = AmbientContentAlpha.current)
                 }
             }
         }
 
-        assertThat(enabledContentColor).isEqualTo(enabledEmphasis.applyEmphasis(onSurface))
-        assertThat(disabledContentColor).isEqualTo(disabledEmphasis.applyEmphasis(onSurface))
+        assertThat(enabledContentColor).isEqualTo(onSurface.copy(alpha = enabledContentAlpha))
+        assertThat(disabledContentColor).isEqualTo(onSurface.copy(alpha = disabledContentAlpha))
     }
 
     @Test
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
index e7286b1..c515542 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
@@ -21,10 +21,11 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.isInMutuallyExclusiveGroup
@@ -37,7 +38,6 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -167,7 +167,7 @@
     private fun assertSelectableAgainstGolden(goldenName: String) {
         // TODO: replace with find(isInMutuallyExclusiveGroup()) after b/157687898 is fixed
         rule.onNodeWithTag(wrapperTestTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenName)
     }
 }
\ No newline at end of file
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 1359aa1..7429bd6 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
@@ -28,11 +28,12 @@
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.LayoutDirection
@@ -40,7 +41,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -584,7 +584,7 @@
     ) {
         // Capture and compare screenshots
         composeTestRule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
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 bd5ad4c..246768b 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
@@ -32,6 +32,7 @@
 import androidx.compose.ui.draw.drawShadow
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInParent
@@ -40,7 +41,7 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performGesture
@@ -370,7 +371,7 @@
         }
 
         rule.onNodeWithTag("Scaffold")
-            .captureToBitmap().apply {
+            .captureToImage().asAndroidBitmap().apply {
                 // asserts the appbar(top half part) has the shadow
                 val yPos = height / 2 + 2
                 assertThat(Color(getPixel(0, yPos))).isNotEqualTo(Color.White)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
index 322bfaf..9bc7687 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
@@ -24,11 +24,12 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.dp
@@ -36,7 +37,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -121,7 +121,7 @@
 
     private fun assertSliderAgainstGolden(goldenName: String) {
         rule.onNodeWithTag(wrapperTestTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenName)
     }
 }
\ No newline at end of file
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 a014b4a..e44cd47 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
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
@@ -30,10 +31,9 @@
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEqualTo
 import androidx.compose.ui.test.assertIsNotEqualTo
-import androidx.compose.ui.test.assertShape
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.getAlignmentLinePosition
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -354,7 +354,7 @@
         }
 
         rule.onNodeWithTag("snackbar")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = shape,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
index f1fb961..5ab421e 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
@@ -23,14 +23,15 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.runtime.emptyContent
+import androidx.compose.testutils.assertPixels
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
@@ -70,7 +71,7 @@
         }
 
         rule.onNodeWithTag("box")
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
@@ -138,8 +139,9 @@
             }
         }
 
-        val topLevelSurfaceBitmap = rule.onNodeWithTag("top level").captureToBitmap()
-        val nestedSurfaceBitmap = rule.onNodeWithTag("nested").captureToBitmap()
+        val topLevelSurfaceBitmap = rule.onNodeWithTag("top level").captureToImage()
+        val nestedSurfaceBitmap = rule.onNodeWithTag("nested").captureToImage()
+            .asAndroidBitmap()
 
         topLevelSurfaceBitmap.assertPixels {
             Color(nestedSurfaceBitmap.getPixel(it.x, it.y))
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
index 8df3b01..ba151db 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
@@ -27,6 +27,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.junit4.StateRestorationTester
@@ -41,6 +43,7 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -62,6 +65,12 @@
     @Before
     fun init() {
         clock = ManualAnimationClock(initTimeMillis = 0L)
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
     }
 
     /**
@@ -1486,6 +1495,32 @@
         }
     }
 
+    @Test
+    fun testInspectorValue() {
+        val state = SwipeableState("A", clock)
+        val anchors = mapOf(0f to "A", 100f to "B")
+        rule.setContent {
+            val modifier = Modifier.swipeable(
+                state = state,
+                anchors = anchors,
+                orientation = Orientation.Horizontal
+            ) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("swipeable")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.map { it.name }.asIterable()).containsExactly(
+                "state",
+                "anchors",
+                "orientation",
+                "enabled",
+                "reverseDirection",
+                "interactionState",
+                "thresholds",
+                "resistance",
+                "velocityThreshold"
+            )
+        }
+    }
+
     private fun swipeRight(
         offset: Float = 100f,
         velocity: Float? = null
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
index 30e0053..5afe047 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
@@ -23,12 +23,13 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.isToggleable
@@ -43,7 +44,6 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -224,7 +224,7 @@
     private fun assertToggeableAgainstGolden(goldenName: String) {
         // TODO: replace with find(isToggeable()) after b/157687898 is fixed
         rule.onNodeWithTag(wrapperTestTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenName)
     }
 }
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
index 39f1b92..c1ba7a5 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
@@ -21,19 +21,19 @@
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -294,7 +294,7 @@
 
         // Capture and compare screenshots
         composeTestRule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
@@ -347,8 +347,7 @@
 ) {
     // Apply default emphasis
     @Suppress("NAME_SHADOWING")
-    val unselectedContentColor = AmbientEmphasisLevels.current.medium
-        .applyEmphasis(unselectedContentColor)
+    val unselectedContentColor = unselectedContentColor.copy(alpha = ContentAlpha.medium)
     Box(Modifier.semantics(mergeAllDescendants = true) {}.testTag(Tag)) {
         TabRow(selectedTabIndex = 0, backgroundColor = backgroundColor) {
             Tab(
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 9117e32..5c377f0 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
@@ -31,6 +31,8 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertCountEquals
 import androidx.compose.ui.test.assertHeightIsEqualTo
@@ -48,6 +50,9 @@
 import androidx.compose.ui.unit.width
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -64,6 +69,16 @@
     @get:Rule
     val rule = createComposeRule()
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     @Test
     fun textTab_height() {
         rule
@@ -426,4 +441,15 @@
                 }
             }
     }
+
+    @Test
+    fun testInspectorValue() {
+        val pos = TabPosition(10.0.dp, 200.0.dp)
+        rule.setContent {
+            val modifier = Modifier.defaultTabIndicatorOffset(pos) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("defaultTabIndicatorOffset")
+            assertThat(modifier.valueOverride).isEqualTo(pos)
+            assertThat(modifier.inspectableElements.asIterable()).isEmpty()
+        }
+    }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/RippleIndicationTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/RippleIndicationTest.kt
index 01d4c89..a3373435 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/RippleIndicationTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/RippleIndicationTest.kt
@@ -37,14 +37,16 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.ComposeTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -53,7 +55,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import com.google.common.truth.Truth
 import org.junit.Rule
 import org.junit.Test
@@ -529,7 +530,7 @@
         }
 
         with(rule.onNodeWithTag(Tag)) {
-            val centerPixel = captureToBitmap()
+            val centerPixel = captureToImage().asAndroidBitmap()
                 .run {
                     getPixel(width / 2, height / 2)
                 }
@@ -548,7 +549,7 @@
         }
 
         with(rule.onNodeWithTag(Tag)) {
-            val centerPixel = captureToBitmap()
+            val centerPixel = captureToImage().asAndroidBitmap()
                 .run {
                     getPixel(width / 2, height / 2)
                 }
@@ -594,7 +595,7 @@
 
         // Capture and compare screenshots
         rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
 
         // Advance until after the end of the ripple animation, so we have a stable final opacity
@@ -604,7 +605,7 @@
 
         // Compare expected and actual pixel color
         val centerPixel = rule.onNodeWithTag(Tag)
-            .captureToBitmap()
+            .captureToImage().asAndroidBitmap()
             .run {
                 getPixel(width / 2, height / 2)
             }
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 f349e2d..1f03cb0 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
@@ -23,11 +23,12 @@
 import androidx.compose.material.Text
 import androidx.compose.material.setMaterialContent
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -40,7 +41,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -168,7 +168,7 @@
 
     private fun assertAgainstGolden(goldenIdentifier: String) {
         rule.onNodeWithTag(TextFieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
\ No newline at end of file
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 be30802..e4c5cf0 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
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.AmbientContentAlpha
 import androidx.compose.material.AmbientContentColor
 import androidx.compose.material.AmbientTextStyle
 import androidx.compose.material.MaterialTheme
@@ -33,6 +34,7 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.isFocused
@@ -45,8 +47,7 @@
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.TextInputServiceAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -407,10 +408,14 @@
                 label = {},
                 placeholder = {
                     Text("placeholder")
-                    assertThat(AmbientContentColor.current)
+                    assertThat(
+                        AmbientContentColor.current.copy(
+                            alpha = AmbientContentAlpha.current
+                        )
+                    )
                         .isEqualTo(
                             MaterialTheme.colors.onSurface.copy(
-                                0.6f
+                                alpha = 0.6f
                             )
                         )
                     assertThat(AmbientTextStyle.current)
@@ -644,7 +649,7 @@
         }
 
         rule.onNodeWithTag(TextfieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldImplTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldImplTest.kt
index d882e00..6ede12a 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldImplTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldImplTest.kt
@@ -24,13 +24,17 @@
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.material.TextFieldScroller
 import androidx.compose.material.TextFieldScrollerPosition
+import androidx.compose.material.iconPadding
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -44,6 +48,8 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -64,6 +70,16 @@
     @get:Rule
     val rule = createComposeRule()
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     @Test
     fun testTextField_scrollable_withLongInput() {
         val scrollerPosition = TextFieldScrollerPosition()
@@ -142,7 +158,7 @@
         rule.runOnIdle {}
 
         rule.onNodeWithTag(TextfieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(parentSize, parentSize)) { position ->
                 if (position.x > textFieldSize && position.y > textFieldSize) Color.White else null
             }
@@ -226,4 +242,15 @@
             assertThat(scrollerPosition.current).isEqualTo(swipePosition)
         }
     }
+
+    @Test
+    fun testInspectorValue() {
+        val modifier = Modifier.iconPadding(10.0.dp, 200.0.dp) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("iconPadding")
+        assertThat(modifier.valueOverride).isNull()
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("start", 10.0.dp),
+            ValueElement("end", 200.0.dp)
+        )
+    }
 }
\ No newline at end of file
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 4b5cca9..005884e 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
@@ -23,11 +23,12 @@
 import androidx.compose.material.TextField
 import androidx.compose.material.setMaterialContent
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -40,7 +41,6 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
-import androidx.test.screenshot.assertAgainstGolden
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -166,7 +166,7 @@
 
     private fun assertAgainstGolden(goldenIdentifier: String) {
         rule.onNodeWithTag(TextFieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
\ No newline at end of file
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 c6ec84f..c5a9e02 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
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.AmbientContentAlpha
 import androidx.compose.material.AmbientContentColor
 import androidx.compose.material.AmbientTextStyle
 import androidx.compose.material.MaterialTheme
@@ -38,6 +39,7 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus
 import androidx.compose.ui.focus.ExperimentalFocus
@@ -56,8 +58,7 @@
 import androidx.compose.ui.platform.ViewAmbient
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHeightIsEqualTo
-import androidx.compose.ui.test.assertShape
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -553,10 +554,14 @@
                 label = {},
                 placeholder = {
                     Text("placeholder")
-                    assertThat(AmbientContentColor.current)
+                    assertThat(
+                        AmbientContentColor.current.copy(
+                            alpha = AmbientContentAlpha.current
+                        )
+                    )
                         .isEqualTo(
                             MaterialTheme.colors.onSurface.copy(
-                                0.6f
+                                alpha = 0.6f
                             )
                         )
                     assertThat(AmbientTextStyle.current)
@@ -797,7 +802,7 @@
         }
 
         rule.onNodeWithTag(TextfieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
@@ -832,7 +837,7 @@
         }
 
         rule.onNodeWithTag(TextfieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
@@ -846,7 +851,7 @@
         assert(latch.await(1, TimeUnit.SECONDS))
 
         rule.onNodeWithTag(TextfieldTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
index 3f99493..0847c43 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AlertDialog.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -150,11 +151,10 @@
             color = backgroundColor,
             contentColor = contentColor
         ) {
-            val emphasisLevels = AmbientEmphasisLevels.current
             Column {
                 if (title != null) {
                     Box(TitlePadding.align(Alignment.Start)) {
-                        ProvideEmphasis(emphasisLevels.high) {
+                        Providers(AmbientContentAlpha provides ContentAlpha.high) {
                             val textStyle = MaterialTheme.typography.subtitle1
                             ProvideTextStyle(textStyle, title)
                         }
@@ -167,7 +167,7 @@
 
                 if (text != null) {
                     Box(TextPadding.align(Alignment.Start)) {
-                        ProvideEmphasis(emphasisLevels.medium) {
+                        Providers(AmbientContentAlpha provides ContentAlpha.medium) {
                             val textStyle = MaterialTheme.typography.body2
                             ProvideTextStyle(textStyle, text)
                         }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AppBar.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AppBar.kt
index 7d8a072..bf782bc 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AppBar.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AppBar.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -78,12 +79,14 @@
     elevation: Dp = TopAppBarElevation
 ) {
     AppBar(backgroundColor, contentColor, elevation, RectangleShape, modifier) {
-        val emphasisLevels = AmbientEmphasisLevels.current
         if (navigationIcon == null) {
             Spacer(TitleInsetWithoutIcon)
         } else {
             Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
-                ProvideEmphasis(emphasisLevels.high, navigationIcon)
+                Providers(
+                    AmbientContentAlpha provides ContentAlpha.high,
+                    children = navigationIcon
+                )
             }
         }
 
@@ -92,11 +95,11 @@
             verticalAlignment = Alignment.CenterVertically
         ) {
             ProvideTextStyle(value = MaterialTheme.typography.h6) {
-                ProvideEmphasis(emphasisLevels.high, title)
+                Providers(AmbientContentAlpha provides ContentAlpha.high, children = title)
             }
         }
 
-        ProvideEmphasis(emphasisLevels.medium) {
+        Providers(AmbientContentAlpha provides ContentAlpha.medium) {
             Row(
                 Modifier.fillMaxHeight(),
                 horizontalArrangement = Arrangement.End,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
index bfb823e..bbd9e40 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
@@ -145,9 +145,7 @@
     alwaysShowLabels: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
     selectedContentColor: Color = AmbientContentColor.current,
-    unselectedContentColor: Color = AmbientEmphasisLevels.current.medium.applyEmphasis(
-        selectedContentColor
-    )
+    unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
 ) {
     val styledLabel = @Composable {
         val style = MaterialTheme.typography.caption.copy(textAlign = TextAlign.Center)
@@ -214,7 +212,10 @@
 
     val color = lerp(inactiveColor, activeColor, animationProgress)
 
-    Providers(AmbientContentColor provides color) {
+    Providers(
+        AmbientContentColor provides color.copy(alpha = 1f),
+        AmbientContentAlpha provides color.alpha,
+    ) {
         content(animationProgress)
     }
 }
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 a321980..aa7388b 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
@@ -37,6 +37,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -106,10 +107,11 @@
     // 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)
     Surface(
         shape = shape,
         color = colors.backgroundColor(enabled),
-        contentColor = colors.contentColor(enabled),
+        contentColor = contentColor.copy(alpha = 1f),
         border = border,
         elevation = elevation?.elevation(enabled, interactionState) ?: 0.dp,
         modifier = modifier.clickable(
@@ -119,21 +121,23 @@
             indication = null
         )
     ) {
-        ProvideTextStyle(
-            value = MaterialTheme.typography.button
-        ) {
-            Row(
-                Modifier
-                    .defaultMinSizeConstraints(
-                        minWidth = ButtonConstants.DefaultMinWidth,
-                        minHeight = ButtonConstants.DefaultMinHeight
-                    )
-                    .indication(interactionState, AmbientIndication.current())
-                    .padding(contentPadding),
-                horizontalArrangement = Arrangement.Center,
-                verticalAlignment = Alignment.CenterVertically,
-                children = content
-            )
+        Providers(AmbientContentAlpha provides contentColor.alpha) {
+            ProvideTextStyle(
+                value = MaterialTheme.typography.button
+            ) {
+                Row(
+                    Modifier
+                        .defaultMinSizeConstraints(
+                            minWidth = ButtonConstants.DefaultMinWidth,
+                            minHeight = ButtonConstants.DefaultMinHeight
+                        )
+                        .indication(interactionState, AmbientIndication.current())
+                        .padding(contentPadding),
+                    horizontalArrangement = Arrangement.Center,
+                    verticalAlignment = Alignment.CenterVertically,
+                    children = content
+                )
+            }
         }
     }
 }
@@ -394,8 +398,8 @@
         disabledBackgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f)
             .compositeOver(MaterialTheme.colors.surface),
         contentColor: Color = contentColorFor(backgroundColor),
-        disabledContentColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(MaterialTheme.colors.onSurface)
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
     ): ButtonColors = DefaultButtonColors(
         backgroundColor,
         disabledBackgroundColor,
@@ -416,8 +420,8 @@
     fun defaultOutlinedButtonColors(
         backgroundColor: Color = MaterialTheme.colors.surface,
         contentColor: Color = MaterialTheme.colors.primary,
-        disabledContentColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(MaterialTheme.colors.onSurface)
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
     ): ButtonColors = DefaultButtonColors(
         backgroundColor,
         backgroundColor,
@@ -438,8 +442,8 @@
     fun defaultTextButtonColors(
         backgroundColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colors.primary,
-        disabledContentColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(MaterialTheme.colors.onSurface)
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
     ): ButtonColors = DefaultButtonColors(
         backgroundColor,
         backgroundColor,
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 39707e1..e7e3d02 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
@@ -205,12 +205,8 @@
         checkedColor: Color = MaterialTheme.colors.secondary,
         uncheckedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
         checkmarkColor: Color = MaterialTheme.colors.surface,
-        disabledColor: Color = AmbientEmphasisLevels.current.disabled.applyEmphasis(
-            MaterialTheme.colors.onSurface
-        ),
-        disabledIndeterminateColor: Color = AmbientEmphasisLevels.current.disabled.applyEmphasis(
-            checkedColor
-        )
+        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled),
+        disabledIndeterminateColor: Color = checkedColor.copy(alpha = ContentAlpha.disabled)
     ): CheckboxColors {
         val clock = AnimationClockAmbient.current.asDisposableClock()
         return remember(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
new file mode 100644
index 0000000..f4e8a3b
--- /dev/null
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.runtime.Composable
+import androidx.compose.runtime.ambientOf
+import androidx.compose.ui.graphics.luminance
+import androidx.compose.ui.util.annotation.FloatRange
+
+/**
+ * Default alpha levels used by Material components.
+ *
+ * See [AmbientContentAlpha].
+ */
+object ContentAlpha {
+    /**
+     * A high level of content alpha, used to represent high emphasis text such as input text in a
+     * selected [TextField].
+     */
+    @Composable
+    val high: Float
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.high,
+            lowContrastAlpha = LowContrastContentAlpha.high
+        )
+
+    /**
+     * A medium level of content alpha, used to represent medium emphasis text such as
+     * placeholder text in a [TextField].
+     */
+    @Composable
+    val medium: Float
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.medium,
+            lowContrastAlpha = LowContrastContentAlpha.medium
+        )
+
+    /**
+     * A low level of content alpha used to represent disabled components, such as text in a
+     * disabled [Button].
+     */
+    @Composable
+    val disabled: Float
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.disabled,
+            lowContrastAlpha = LowContrastContentAlpha.disabled
+        )
+
+    /**
+     * This default implementation uses separate alpha levels depending on the luminance of the
+     * incoming color, and whether the theme is light or dark. This is to ensure correct contrast
+     * and accessibility on all surfaces.
+     *
+     * See [HighContrastContentAlpha] and [LowContrastContentAlpha] for what the levels are
+     * used for, and under what circumstances.
+     */
+    @Composable
+    private fun contentAlpha(
+        @FloatRange(from = 0.0, to = 1.0) highContrastAlpha: Float,
+        @FloatRange(from = 0.0, to = 1.0) lowContrastAlpha: Float
+    ): Float {
+        val contentColor = AmbientContentColor.current
+        val lightTheme = MaterialTheme.colors.isLight
+        return if (lightTheme) {
+            if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
+        } else {
+            if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
+        }
+    }
+}
+
+/**
+ * Ambient containing the preferred content alpha for a given position in the hierarchy. This
+ * alpha is used for text and iconography ([Text] and [Icon]) to emphasize / de-emphasize
+ * different parts of a component. See the Material guide on
+ * [Text Legibility](https://material.io/design/color/text-legibility.html) for more information on
+ * alpha levels used by text and iconography.
+ *
+ * See [ContentAlpha] for the default levels used by most Material components.
+ *
+ * [MaterialTheme] sets this to [ContentAlpha.high] by default, as this is the default alpha for
+ * body text.
+ *
+ * @sample androidx.compose.material.samples.ContentAlphaSample
+ */
+val AmbientContentAlpha = ambientOf { 1f }
+
+/**
+ * Alpha levels for high luminance content in light theme, or low luminance content in dark theme.
+ *
+ * This content will typically be placed on colored surfaces, so it is important that the
+ * contrast here is higher to meet accessibility standards, and increase legibility.
+ *
+ * These levels are typically used for text / iconography in primary colored tabs /
+ * bottom navigation / etc.
+ */
+private object HighContrastContentAlpha {
+    const val high: Float = 1.00f
+    const val medium: Float = 0.74f
+    const val disabled: Float = 0.38f
+}
+
+/**
+ * Alpha levels for low luminance content in light theme, or high luminance content in dark theme.
+ *
+ * This content will typically be placed on grayscale surfaces, so the contrast here can be lower
+ * without sacrificing accessibility and legibility.
+ *
+ * These levels are typically used for body text on the main surface (white in light theme, grey
+ * in dark theme) and text / iconography in surface colored tabs / bottom navigation / etc.
+ */
+private object LowContrastContentAlpha {
+    const val high: Float = 0.87f
+    const val medium: Float = 0.60f
+    const val disabled: Float = 0.38f
+}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Emphasis.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Emphasis.kt
index ac5e569..e4d5097 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Emphasis.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Emphasis.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION")
+
 package androidx.compose.material
 
 import androidx.compose.runtime.Ambient
@@ -46,6 +48,9 @@
  * For more information on emphasis and ensuring legibility for content, see
  * [Text legibility](https://material.io/design/color/text-legibility.html)
  */
+@Deprecated(
+    message = "Emphasis has been simplified and replaced with ContentAlpha"
+)
 @Immutable
 interface Emphasis {
     /**
@@ -68,6 +73,9 @@
  *
  * See [AmbientEmphasisLevels] to retrieve the current [EmphasisLevels]
  */
+@Deprecated(
+    message = "Emphasis has been simplified and replaced with ContentAlpha"
+)
 interface EmphasisLevels {
     /**
      * Emphasis used to express high emphasis, such as for selected text fields.
@@ -91,15 +99,24 @@
  *
  * See [AmbientEmphasisLevels] to retrieve the levels of emphasis provided in the theme,
  * so they can be applied with this function.
- *
- * @sample androidx.compose.material.samples.EmphasisSample
  */
+@Deprecated(
+    message = "Emphasis has been simplified and replaced with ContentAlpha",
+    replaceWith = ReplaceWith(
+        "Providers(AmbientContentAlpha provides ContentAlpha.high, children = content)",
+        "androidx.compose.runtime.Providers",
+        "androidx.compose.material.ContentAlpha"
+    )
+)
 @Composable
 fun ProvideEmphasis(emphasis: Emphasis, content: @Composable () -> Unit) {
     val emphasizedColor = emphasis.applyEmphasis(AmbientContentColor.current)
     Providers(AmbientContentColor provides emphasizedColor, children = content)
 }
 
+@Deprecated(
+    message = "Emphasis has been simplified and replaced with ContentAlpha"
+)
 /**
  * Ambient containing the current [EmphasisLevels] in this hierarchy.
  */
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 cbe832f..352d434 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
@@ -35,6 +35,7 @@
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -95,13 +96,15 @@
         contentColor = contentColor,
         elevation = elevation.elevation(interactionState)
     ) {
-        ProvideTextStyle(MaterialTheme.typography.button) {
-            Box(
-                modifier = Modifier
-                    .defaultMinSizeConstraints(minWidth = FabSize, minHeight = FabSize)
-                    .indication(interactionState, AmbientIndication.current()),
-                alignment = Alignment.Center
-            ) { icon() }
+        Providers(AmbientContentAlpha provides contentColor.alpha) {
+            ProvideTextStyle(MaterialTheme.typography.button) {
+                Box(
+                    modifier = Modifier
+                        .defaultMinSizeConstraints(minWidth = FabSize, minHeight = FabSize)
+                        .indication(interactionState, AmbientIndication.current()),
+                    alignment = Alignment.Center
+                ) { icon() }
+            }
         }
     }
 }
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 c8b394b..105e998 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
@@ -45,7 +45,7 @@
 fun Icon(
     asset: VectorAsset,
     modifier: Modifier = Modifier,
-    tint: Color = AmbientContentColor.current
+    tint: Color = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
 ) {
     Icon(
         painter = rememberVectorPainter(asset),
@@ -90,7 +90,7 @@
 fun Icon(
     painter: Painter,
     modifier: Modifier = Modifier,
-    tint: Color = AmbientContentColor.current
+    tint: Color = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
 ) {
     // TODO: consider allowing developers to override the intrinsic size, and specify their own
     // size that this icon will be forced to take up.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ListItem.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ListItem.kt
index f3d9783..1bde0a7 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ListItem.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ListItem.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.preferredSizeIn
 import androidx.compose.foundation.layout.preferredWidthIn
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.AlignmentLine
@@ -70,13 +71,12 @@
     trailing: @Composable (() -> Unit)? = null,
     text: @Composable () -> Unit
 ) {
-    val emphasisLevels = AmbientEmphasisLevels.current
     val typography = MaterialTheme.typography
 
-    val styledText = applyTextStyle(typography.subtitle1, emphasisLevels.high, text)!!
-    val styledSecondaryText = applyTextStyle(typography.body2, emphasisLevels.medium, secondaryText)
-    val styledOverlineText = applyTextStyle(typography.overline, emphasisLevels.high, overlineText)
-    val styledTrailing = applyTextStyle(typography.caption, emphasisLevels.high, trailing)
+    val styledText = applyTextStyle(typography.subtitle1, ContentAlpha.high, text)!!
+    val styledSecondaryText = applyTextStyle(typography.body2, ContentAlpha.medium, secondaryText)
+    val styledOverlineText = applyTextStyle(typography.overline, ContentAlpha.high, overlineText)
+    val styledTrailing = applyTextStyle(typography.caption, ContentAlpha.high, trailing)
 
     val semanticsModifier = modifier.semantics(mergeAllDescendants = true) {}
 
@@ -405,12 +405,12 @@
 
 private fun applyTextStyle(
     textStyle: TextStyle,
-    emphasis: Emphasis,
+    contentAlpha: Float,
     icon: @Composable (() -> Unit)?
 ): @Composable (() -> Unit)? {
     if (icon == null) return null
     return {
-        ProvideEmphasis(emphasis) {
+        Providers(AmbientContentAlpha provides contentAlpha) {
             ProvideTextStyle(textStyle, icon)
         }
     }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
index 7d89096..579d5eb 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
@@ -66,7 +66,8 @@
         AmbientColors provides rememberedColors,
         AmbientIndication provides indicationFactory,
         AmbientTypography provides typography,
-        AmbientShapes provides shapes
+        AmbientShapes provides shapes,
+        AmbientContentAlpha provides ContentAlpha.high
     ) {
         ProvideTextStyle(value = typography.body1, children = content)
     }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
index 8e793cc..efc04ce 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
@@ -36,6 +36,7 @@
 import androidx.compose.material.ripple.RippleIndication
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Providers
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -187,14 +188,10 @@
             .padding(horizontal = DropdownMenuHorizontalPadding),
         alignment = Alignment.CenterStart
     ) {
-        // TODO(popam, b/156912039): update emphasis if the menu item is disabled
         val typography = MaterialTheme.typography
-        val emphasisLevels = AmbientEmphasisLevels.current
         ProvideTextStyle(typography.subtitle1) {
-            ProvideEmphasis(
-                if (enabled) emphasisLevels.high else emphasisLevels.disabled,
-                content
-            )
+            val contentAlpha = if (enabled) ContentAlpha.high else ContentAlpha.disabled
+            Providers(AmbientContentAlpha provides contentAlpha, children = content)
         }
     }
 }
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 a225446..ec360b1 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
@@ -137,9 +137,7 @@
     fun defaultColors(
         selectedColor: Color = MaterialTheme.colors.secondary,
         unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
-        disabledColor: Color = AmbientEmphasisLevels.current.disabled.applyEmphasis(
-            MaterialTheme.colors.onSurface
-        )
+        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
     ): RadioButtonColors {
         val clock = AnimationClockAmbient.current.asDisposableClock()
         return remember(
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 d74d847..6fd3ca9 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
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.paddingFromBaseline
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -91,7 +92,7 @@
         color = backgroundColor,
         contentColor = contentColor
     ) {
-        ProvideEmphasis(AmbientEmphasisLevels.current.high) {
+        Providers(AmbientContentAlpha provides ContentAlpha.high) {
             val textStyle = MaterialTheme.typography.body2
             ProvideTextStyle(value = textStyle) {
                 when {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
index dfc980f..6fbd392 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
@@ -45,6 +45,7 @@
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -434,7 +435,20 @@
     thresholds: (from: T, to: T) -> ThresholdConfig = { _, _ -> FixedThreshold(56.dp) },
     resistance: ResistanceConfig? = defaultResistanceConfig(anchors.keys),
     velocityThreshold: Dp = DefaultVelocityThreshold
-) = composed {
+) = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "swipeable"
+        properties["state"] = state
+        properties["anchors"] = anchors
+        properties["orientation"] = orientation
+        properties["enabled"] = enabled
+        properties["reverseDirection"] = reverseDirection
+        properties["interactionState"] = interactionState
+        properties["thresholds"] = thresholds
+        properties["resistance"] = resistance
+        properties["velocityThreshold"] = velocityThreshold
+    }
+) {
     require(anchors.isNotEmpty()) {
         "You must have at least one anchor."
     }
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 822b37a..91aa21d 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
@@ -235,17 +235,17 @@
         uncheckedThumbColor: Color = MaterialTheme.colors.surface,
         uncheckedTrackColor: Color = MaterialTheme.colors.onSurface,
         uncheckedTrackAlpha: Float = 0.38f,
-        disabledCheckedThumbColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(checkedThumbColor)
+        disabledCheckedThumbColor: Color = checkedThumbColor
+            .copy(alpha = ContentAlpha.disabled)
             .compositeOver(MaterialTheme.colors.surface),
-        disabledCheckedTrackColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(checkedTrackColor)
+        disabledCheckedTrackColor: Color = checkedTrackColor
+            .copy(alpha = ContentAlpha.disabled)
             .compositeOver(MaterialTheme.colors.surface),
-        disabledUncheckedThumbColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(uncheckedThumbColor)
+        disabledUncheckedThumbColor: Color = uncheckedThumbColor
+            .copy(alpha = ContentAlpha.disabled)
             .compositeOver(MaterialTheme.colors.surface),
-        disabledUncheckedTrackColor: Color = AmbientEmphasisLevels.current.disabled
-            .applyEmphasis(uncheckedTrackColor)
+        disabledUncheckedTrackColor: Color = uncheckedTrackColor
+            .copy(alpha = ContentAlpha.disabled)
             .compositeOver(MaterialTheme.colors.surface)
     ): SwitchColors = DefaultSwitchColors(
         checkedThumbColor = checkedThumbColor,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt
index d289dff..ce168b17 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt
@@ -52,6 +52,7 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.id
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
@@ -91,9 +92,7 @@
     icon: @Composable () -> Unit = emptyContent(),
     interactionState: InteractionState = remember { InteractionState() },
     selectedContentColor: Color = AmbientContentColor.current,
-    unselectedContentColor: Color = AmbientEmphasisLevels.current.medium.applyEmphasis(
-        selectedContentColor
-    )
+    unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
 ) {
     val styledText = @Composable {
         val style = MaterialTheme.typography.button.copy(textAlign = TextAlign.Center)
@@ -139,9 +138,7 @@
     modifier: Modifier = Modifier,
     interactionState: InteractionState = remember { InteractionState() },
     selectedContentColor: Color = AmbientContentColor.current,
-    unselectedContentColor: Color = AmbientEmphasisLevels.current.medium.applyEmphasis(
-        selectedContentColor
-    ),
+    unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium),
     content: @Composable ColumnScope.() -> Unit
 ) {
     // The color of the Ripple should always the selected color, as we want to show the color
@@ -218,7 +215,12 @@
      */
     fun Modifier.defaultTabIndicatorOffset(
         currentTabPosition: TabPosition
-    ): Modifier = composed {
+    ): Modifier = composed(
+        inspectorInfo = debugInspectorInfo {
+            name = "defaultTabIndicatorOffset"
+            value = currentTabPosition
+        }
+    ) {
         // TODO: should we animate the width of the indicator as it moves between tabs of different
         // sizes inside a scrollable tab row?
         val currentTabWidth = currentTabPosition.width
@@ -294,7 +296,12 @@
         }
     }
     val state = transition(transitionDefinition, selected)
-    Providers(AmbientContentColor provides state[TabTintColor], children = content)
+    val color = state[TabTintColor]
+    Providers(
+        AmbientContentColor provides color.copy(alpha = 1f),
+        AmbientContentAlpha provides color.alpha,
+        children = content
+    )
 }
 
 /**
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
index ef13fc2..50cbd6e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Text.kt
@@ -53,8 +53,9 @@
  * from [style] will be used instead.
  *
  * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
- * [AmbientContentColor] will be used - this allows this [Text] or element containing this [Text] to
- * adapt to different background colors and still maintain contrast and accessibility.
+ * [AmbientContentColor] will be used with an alpha of [AmbientContentAlpha]- this allows this
+ * [Text] or element containing this [Text] to adapt to different background colors and still
+ * maintain contrast and accessibility.
  *
  * @param text The text to be displayed.
  * @param modifier [Modifier] to apply to this layout node.
@@ -139,8 +140,9 @@
  * from [style] will be used instead.
  *
  * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
- * [AmbientContentColor] will be used - this allows this [Text] or element containing this [Text] to
- * adapt to different background colors and still maintain contrast and accessibility.
+ * [AmbientContentColor] will be used with an alpha of [AmbientContentAlpha]- this allows this
+ * [Text] or element containing this [Text] to adapt to different background colors and still
+ * maintain contrast and accessibility.
  *
  * @param text The text to be displayed.
  * @param modifier [Modifier] to apply to this layout node.
@@ -191,7 +193,11 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     style: TextStyle = AmbientTextStyle.current
 ) {
-    val textColor = color.useOrElse { style.color.useOrElse { AmbientContentColor.current } }
+    val textColor = color.useOrElse {
+        style.color.useOrElse {
+            AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
+        }
+    }
     val mergedStyle = style.merge(
         TextStyle(
             color = textColor,
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 cc81df7..9203601 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
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/160821157): Replace FocusDetailedState with FocusState2 DEPRECATION
-@file:Suppress("DEPRECATION")
-
 package androidx.compose.material
 
 import androidx.compose.animation.ColorPropKey
@@ -66,6 +63,8 @@
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.node.Ref
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.text.SoftwareKeyboardController
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
@@ -129,7 +128,7 @@
         Decoration(
             contentColor = inactiveColor,
             typography = MaterialTheme.typography.subtitle1,
-            emphasis = AmbientEmphasisLevels.current.high
+            contentAlpha = ContentAlpha.high
         ) {
             TextFieldScroller(
                 scrollerPosition = rememberSavedInstanceState(
@@ -176,25 +175,23 @@
             }
         }
 
-    val emphasisLevels = AmbientEmphasisLevels.current
-
     TextFieldTransitionScope.transition(
         inputState = inputState,
         showLabel = label != null,
         activeColor = if (isErrorValue) {
             errorColor
         } else {
-            emphasisLevels.high.applyEmphasis(activeColor)
+            activeColor.applyAlpha(alpha = ContentAlpha.high)
         },
         labelInactiveColor = if (isErrorValue) {
             errorColor
         } else {
-            emphasisLevels.medium.applyEmphasis(inactiveColor)
+            inactiveColor.applyAlpha(alpha = ContentAlpha.medium)
         },
         indicatorInactiveColor = when {
             isErrorValue -> errorColor
             type == TextFieldType.Filled -> inactiveColor.applyAlpha(alpha = IndicatorInactiveAlpha)
-            else -> emphasisLevels.disabled.applyEmphasis(inactiveColor)
+            else -> inactiveColor.applyAlpha(alpha = ContentAlpha.disabled)
         }
 
     ) { labelProgress, animatedLabelColor, indicatorWidth, indicatorColor, placeholderOpacity ->
@@ -225,7 +222,7 @@
                         Decoration(
                             contentColor = inactiveColor,
                             typography = MaterialTheme.typography.subtitle1,
-                            emphasis = AmbientEmphasisLevels.current.medium,
+                            contentAlpha = ContentAlpha.medium,
                             children = placeholder
                         )
                     }
@@ -358,14 +355,14 @@
 internal fun Decoration(
     contentColor: Color,
     typography: TextStyle? = null,
-    emphasis: Emphasis? = null,
+    contentAlpha: Float? = null,
     children: @Composable () -> Unit
 ) {
     val colorAndEmphasis = @Composable {
         Providers(AmbientContentColor provides contentColor) {
-            if (emphasis != null) ProvideEmphasis(
-                emphasis,
-                children
+            if (contentAlpha != null) Providers(
+                AmbientContentAlpha provides contentAlpha,
+                children = children
             ) else children()
         }
     }
@@ -380,23 +377,31 @@
  * A modifier that applies padding only if the size of the element is not zero
  */
 internal fun Modifier.iconPadding(start: Dp = 0.dp, end: Dp = 0.dp) =
-    this.then(object : LayoutModifier {
-        override fun MeasureScope.measure(
-            measurable: Measurable,
-            constraints: Constraints
-        ): MeasureResult {
-            val horizontal = start.toIntPx() + end.toIntPx()
-            val placeable = measurable.measure(constraints.offset(-horizontal))
-            val width = if (placeable.nonZero) {
-                constraints.constrainWidth(placeable.width + horizontal)
-            } else {
-                0
+    this.then(
+        object : LayoutModifier, InspectorValueInfo(
+            debugInspectorInfo {
+                name = "iconPadding"
+                properties["start"] = start
+                properties["end"] = end
             }
-            return layout(width, placeable.height) {
-                placeable.placeRelative(start.toIntPx(), 0)
+        ) {
+            override fun MeasureScope.measure(
+                measurable: Measurable,
+                constraints: Constraints
+            ): MeasureResult {
+                val horizontal = start.toIntPx() + end.toIntPx()
+                val placeable = measurable.measure(constraints.offset(-horizontal))
+                val width = if (placeable.nonZero) {
+                    constraints.constrainWidth(placeable.width + horizontal)
+                } else {
+                    0
+                }
+                return layout(width, placeable.height) {
+                    placeable.placeRelative(start.toIntPx(), 0)
+                }
             }
         }
-    })
+    )
 
 private object TextFieldTransitionScope {
     private val LabelColorProp = ColorPropKey()
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index dfb5e59..55390d2 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -541,8 +541,8 @@
   public final class TraceKt {
   }
 
-  @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
-    method public abstract Class<?>[] types();
+  @Deprecated @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
+    method @Deprecated public abstract Class<?>[] types();
     property public abstract Class<?>![] types;
   }
 
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index dfb5e59..55390d2 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -541,8 +541,8 @@
   public final class TraceKt {
   }
 
-  @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
-    method public abstract Class<?>[] types();
+  @Deprecated @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
+    method @Deprecated public abstract Class<?>[] types();
     property public abstract Class<?>![] types;
   }
 
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index a61a7b4..547521d 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -569,8 +569,8 @@
   public final class TraceKt {
   }
 
-  @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
-    method public abstract Class<?>[] types();
+  @Deprecated @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface UnionType {
+    method @Deprecated public abstract Class<?>[] types();
     property public abstract Class<?>![] types;
   }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 2d01ff6..776fbdf 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -732,37 +732,33 @@
         }
 
         fun dispatchLifecycleObservers() {
-            trace("Compose:lifecycles") {
-                // Send lifecycle leaves
-                if (leaves.isNotEmpty()) {
-                    for (holder in leaves.reversed()) {
-                        // The count of the holder might be greater than 0 here as it might leave one
-                        // part of the composition and reappear in another. Only send a leave if the
-                        // count is still 0 after all changes have been applied.
-                        if (holder.count == 0) {
-                            holder.instance.onLeave()
-                            lifecycleObservers.remove(holder)
-                        }
+            // Send lifecycle leaves
+            if (leaves.isNotEmpty()) {
+                for (holder in leaves.reversed()) {
+                    // The count of the holder might be greater than 0 here as it might leave one
+                    // part of the composition and reappear in another. Only send a leave if the
+                    // count is still 0 after all changes have been applied.
+                    if (holder.count == 0) {
+                        holder.instance.onLeave()
+                        lifecycleObservers.remove(holder)
                     }
                 }
+            }
 
-                // Send lifecycle enters
-                if (enters.isNotEmpty()) {
-                    for (holder in enters) {
-                        holder.instance.onEnter()
-                    }
+            // Send lifecycle enters
+            if (enters.isNotEmpty()) {
+                for (holder in enters) {
+                    holder.instance.onEnter()
                 }
             }
         }
 
         fun dispatchSideEffects() {
-            trace("Compose:sideEffects") {
-                if (sideEffects.isNotEmpty()) {
-                    for (sideEffect in sideEffects) {
-                        sideEffect()
-                    }
-                    sideEffects.clear()
+            if (sideEffects.isNotEmpty()) {
+                for (sideEffect in sideEffects) {
+                    sideEffect()
                 }
+                sideEffects.clear()
             }
         }
     }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
index ad60a95..563bf4a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
@@ -31,4 +31,5 @@
 @Retention(AnnotationRetention.BINARY)
 @Target(AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER)
 @ExperimentalComposeApi
+@Deprecated("Union Types are no longer supported by the Compose Compiler")
 annotation class UnionType(vararg val types: KClass<*>)
diff --git a/compose/test-utils/build.gradle b/compose/test-utils/build.gradle
index 316de68..9fac472 100644
--- a/compose/test-utils/build.gradle
+++ b/compose/test-utils/build.gradle
@@ -39,6 +39,7 @@
          */
 
         api "androidx.activity:activity:1.2.0-alpha02"
+        api project(":test-screenshot")
 
         implementation(KOTLIN_STDLIB_COMMON)
         implementation project(":benchmark:benchmark-junit4")
@@ -76,6 +77,7 @@
 
             androidMain.dependencies {
                 api "androidx.activity:activity:1.2.0-alpha02"
+                api project(":test-screenshot")
                 // This has stub APIs for access to legacy Android APIs, so we don't want
                 // any dependency on this module.
                 compileOnly project(":compose:ui:ui-android-stubs")
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt
new file mode 100644
index 0000000..db0e572
--- /dev/null
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.testutils
+
+import android.graphics.Bitmap
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.asImageAsset
+import androidx.compose.ui.test.assertContainsColor
+import androidx.compose.ui.test.assertPixelColor
+import androidx.compose.ui.test.assertPixels
+import androidx.compose.ui.test.assertShape
+import androidx.compose.ui.test.contains
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+
+/**
+ * A helper function to run asserts on [Bitmap].
+ *
+ * @param expectedSize The expected size of the bitmap. Leave null to skip the check.
+ * @param expectedColorProvider Returns the expected color for the provided pixel position.
+ * The returned color is then asserted as the expected one on the given bitmap.
+ *
+ * @throws AssertionError if size or colors don't match.
+ */
+@Suppress("DEPRECATION")
+fun ImageAsset.assertPixels(
+    expectedSize: IntSize? = null,
+    expectedColorProvider: (pos: IntOffset) -> Color?
+) = asAndroidBitmap().assertPixels(expectedSize, expectedColorProvider)
+
+/**
+ * Asserts that the color at a specific pixel in the bitmap at ([x], [y]) is [expected].
+ */
+@Suppress("DEPRECATION")
+fun ImageAsset.assertPixelColor(
+    expected: Color,
+    x: Int,
+    y: Int,
+    error: (Color) -> String = { color -> "Pixel($x, $y) expected to be $expected, but was $color" }
+) = asAndroidBitmap().assertPixelColor(expected, x, y, error)
+
+/**
+ * Asserts that the expected color is present in this bitmap.
+ *
+ * @throws AssertionError if the expected color is not present.
+ */
+@Suppress("DEPRECATION")
+fun ImageAsset.assertContainsColor(
+    expectedColor: Color
+) = asAndroidBitmap().assertContainsColor(expectedColor).asImageAsset()
+
+/**
+ * Tests to see if the given point is within the path. (That is, whether the
+ * point would be in the visible portion of the path if the path was used
+ * with [Canvas.clipPath].)
+ *
+ * The `point` argument is interpreted as an offset from the origin.
+ *
+ * Returns true if the point is in the path, and false otherwise.
+ */
+@Suppress("DEPRECATION")
+fun Path.contains(offset: Offset): Boolean = contains(offset)
+
+/**
+ * Asserts that the given [shape] is drawn within the bitmap with the size the dimensions
+ * [shapeSizeX] x [shapeSizeY], centered at ([centerX], [centerY]) with the color [shapeColor].
+ * The bitmap area examined is [sizeX] x [sizeY], centered at ([centerX], [centerY]) and everything
+ * outside the shape is expected to be color [backgroundColor].
+ *
+ * @param density current [Density] or the screen
+ * @param shape defines the [Shape]
+ * @param shapeColor the color of the shape
+ * @param backgroundColor the color of the background
+ * @param backgroundShape defines the [Shape] of the background
+ * @param sizeX width of the area filled with the [backgroundShape]
+ * @param sizeY height of the area filled with the [backgroundShape]
+ * @param shapeSizeX width of the area filled with the [shape]
+ * @param shapeSizeY height of the area filled with the [shape]
+ * @param centerX the X position of the center of the [shape] inside the [sizeX]
+ * @param centerY the Y position of the center of the [shape] inside the [sizeY]
+ * @param shapeOverlapPixelCount The size of the border area from the shape outline to leave it
+ * untested as it is likely anti-aliased. The default is 1 pixel
+ */
+// TODO (mount, malkov) : to investigate why it flakes when shape is not rect
+@Suppress("DEPRECATION")
+fun ImageAsset.assertShape(
+    density: Density,
+    shape: Shape,
+    shapeColor: Color,
+    backgroundColor: Color,
+    backgroundShape: Shape = RectangleShape,
+    sizeX: Float = width.toFloat(),
+    sizeY: Float = height.toFloat(),
+    shapeSizeX: Float = sizeX,
+    shapeSizeY: Float = sizeY,
+    centerX: Float = width / 2f,
+    centerY: Float = height / 2f,
+    shapeOverlapPixelCount: Float = 1.0f
+) = asAndroidBitmap().assertShape(
+    density,
+    shape,
+    shapeColor,
+    backgroundColor,
+    backgroundShape,
+    sizeX,
+    sizeY,
+    shapeSizeX,
+    shapeSizeY,
+    centerX,
+    centerY,
+    shapeOverlapPixelCount
+)
+
+/**
+ * Asserts that the bitmap is fully occupied by the given [shape] with the color [shapeColor]
+ * without [horizontalPadding] and [verticalPadding] from the sides. The padded area is expected
+ * to have [backgroundColor].
+ *
+ * @param density current [Density] or the screen
+ * @param horizontalPadding the symmetrical padding to be applied from both left and right sides
+ * @param verticalPadding the symmetrical padding to be applied from both top and bottom sides
+ * @param backgroundColor the color of the background
+ * @param shapeColor the color of the shape
+ * @param shape defines the [Shape]
+ * @param shapeOverlapPixelCount The size of the border area from the shape outline to leave it
+ * untested as it is likely anti-aliased. The default is 1 pixel
+ */
+@Suppress("DEPRECATION")
+fun ImageAsset.assertShape(
+    density: Density,
+    horizontalPadding: Dp,
+    verticalPadding: Dp,
+    backgroundColor: Color,
+    shapeColor: Color,
+    shape: Shape = RectangleShape,
+    shapeOverlapPixelCount: Float = 1.0f
+) = asAndroidBitmap().assertShape(
+    density,
+    horizontalPadding,
+    verticalPadding,
+    backgroundColor,
+    shapeColor,
+    shape,
+    shapeOverlapPixelCount
+)
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt
new file mode 100644
index 0000000..b0da4ba4
--- /dev/null
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.testutils
+
+import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.test.screenshot.ScreenshotTestRule
+import androidx.test.screenshot.assertAgainstGolden
+import androidx.test.screenshot.matchers.BitmapMatcher
+import androidx.test.screenshot.matchers.MSSIMMatcher
+
+/**
+ * Asserts this bitmap against the golden identified by the given name.
+ *
+ * Note: The golden identifier should be unique per your test module (unless you want multiple tests
+ * to match the same golden). The name must not contain extension. You should also avoid adding
+ * strings like "golden", "image" and instead describe what is the golder referring to.
+ *
+ * @param rule The screenshot test rule that provides the comparison and reporting.
+ * @param goldenIdentifier Name of the golden. Allowed characters: 'A-Za-z0-9_-'
+ * @param matcher The algorithm to be used to perform the matching. By default [MSSIMMatcher]
+ * is used.
+ */
+fun ImageAsset.assertAgainstGolden(
+    rule: ScreenshotTestRule,
+    goldenIdentifier: String,
+    matcher: BitmapMatcher = MSSIMMatcher()
+) = asAndroidBitmap().assertAgainstGolden(rule, goldenIdentifier, matcher)
\ No newline at end of file
diff --git a/compose/ui/ui-geometry/api/current.txt b/compose/ui/ui-geometry/api/current.txt
index fa734db..8135cb2 100644
--- a/compose/ui/ui-geometry/api/current.txt
+++ b/compose/ui/ui-geometry/api/current.txt
@@ -273,7 +273,7 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Size? lerp-3tf5JpU(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline operator long times-2DtskRk(float, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-B9jgaKk(double, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-cEP68aU(int, long size);
diff --git a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
index fa734db..8135cb2 100644
--- a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
@@ -273,7 +273,7 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Size? lerp-3tf5JpU(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline operator long times-2DtskRk(float, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-B9jgaKk(double, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-cEP68aU(int, long size);
diff --git a/compose/ui/ui-geometry/api/restricted_current.txt b/compose/ui/ui-geometry/api/restricted_current.txt
index fa734db..8135cb2 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -273,7 +273,7 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Size? lerp-3tf5JpU(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline operator long times-2DtskRk(float, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-B9jgaKk(double, long size);
     method @androidx.compose.runtime.Stable public static inline operator long times-cEP68aU(int, long size);
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
index 3042637..2df0db1 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
@@ -154,7 +154,7 @@
  * an `AnimationController`.
  */
 @Stable
-fun lerp(start: Size, stop: Size, fraction: Float): Size? {
+fun lerp(start: Size, stop: Size, fraction: Float): Size {
     return Size(
         lerp(start.width, stop.width, fraction),
         lerp(start.height, stop.height, fraction)
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/SizeTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/SizeTest.kt
index 30371d4..35592f3 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/SizeTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/SizeTest.kt
@@ -132,4 +132,11 @@
             // no-op
         }
     }
+
+    @Test
+    fun testSizeLerp() {
+        val size1 = Size(100f, 200f)
+        val size2 = Size(300f, 500f)
+        Assert.assertEquals(Size(200f, 350f), lerp(size1, size2, 0.5f))
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
index 5b2c2c5..2e47f48 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
@@ -28,16 +28,16 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.test.captureToImage
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
-import androidx.compose.ui.test.captureToBitmap
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 
@@ -77,7 +77,7 @@
         // Not sure why this test is flaky, so this is just going to make sure that
         // the drawn content can get onto the screen before we capture the bitmap.
         activityTestRule.runOnUiThread { }
-        val bitmap = groupView!!.captureToBitmap()
+        val bitmap = groupView!!.captureToImage().asAndroidBitmap()
         assertEquals(Color.WHITE, bitmap.getPixel(0, 0))
         assertEquals(Color.WHITE, bitmap.getPixel(9, 9))
         assertNotEquals(Color.WHITE, bitmap.getPixel(10, 10))
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 3071cae..28617e1 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -13,14 +13,16 @@
   }
 
   public final class AndroidBitmapHelpersKt {
-    method public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
-    method public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
-    method public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
-    method public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
-    method public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
+    method @Deprecated public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
+    method @Deprecated public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
+    method @Deprecated public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
+    method @Deprecated public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
+    method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
   public final class AndroidInputDispatcherKt {
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 3071cae..28617e1 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -13,14 +13,16 @@
   }
 
   public final class AndroidBitmapHelpersKt {
-    method public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
-    method public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
-    method public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
-    method public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
-    method public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
+    method @Deprecated public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
+    method @Deprecated public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
+    method @Deprecated public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
+    method @Deprecated public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
+    method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
   public final class AndroidInputDispatcherKt {
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 3071cae..28617e1 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -13,14 +13,16 @@
   }
 
   public final class AndroidBitmapHelpersKt {
-    method public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
-    method public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
-    method public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
-    method public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
-    method public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
+    method @Deprecated public static android.graphics.Bitmap assertContainsColor-VZNGtkI(android.graphics.Bitmap, long expectedColor);
+    method @Deprecated public static void assertPixelColor-eWrXCG0(android.graphics.Bitmap, long expected, int x, int y, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,java.lang.String> error);
+    method @Deprecated public static void assertPixels-mvPU1zk(android.graphics.Bitmap, optional androidx.compose.ui.unit.IntSize? expectedSize, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,androidx.compose.ui.graphics.Color> expectedColorProvider);
+    method @Deprecated public static void assertShape-LBqplUo(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, androidx.compose.ui.graphics.Shape shape, long shapeColor, long backgroundColor, optional androidx.compose.ui.graphics.Shape backgroundShape, optional float sizeX, optional float sizeY, optional float shapeSizeX, optional float shapeSizeY, optional float centerX, optional float centerY, optional float shapeOverlapPixelCount);
+    method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
   public final class AndroidInputDispatcherKt {
diff --git a/compose/ui/ui-test/lint-baseline.xml b/compose/ui/ui-test/lint-baseline.xml
index c6e4a4d..ad8b38c 100644
--- a/compose/ui/ui-test/lint-baseline.xml
+++ b/compose/ui/ui-test/lint-baseline.xml
@@ -2,6 +2,17 @@
 <issues format="5" by="lint 4.2.0-alpha06" client="gradle" variant="debug" version="4.2.0-alpha06">
 
     <issue
+        id="IllegalExperimentalApiUsage"
+        message="Experimental/OptIn APIs should only be used from within the same library or libraries within the same requireSameVersion group"
+        errorLine1="    performKeyPress(keyEvent)"
+        errorLine2="    ~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/ui/test/ActionsStubs.kt"
+            line="44"
+            column="5"/>
+    </issue>
+
+    <issue
         id="UnsafeNewApiCall"
         message="This call is to a method from API 29, the call containing class androidx.compose.ui.test.android.WindowCaptureKt is not annotated with @RequiresApi(x) where x is at least 29. Either annotate the containing class with at least @RequiresApi(29) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(29)."
         errorLine1="            decorView.viewTreeObserver.registerFrameCommitCallback {"
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/BitmapCapturingTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/BitmapCapturingTest.kt
index 75a7e1b..959d16d 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/BitmapCapturingTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/BitmapCapturingTest.kt
@@ -27,6 +27,8 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.material.AlertDialog
 import androidx.compose.material.Text
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertPixels
 import androidx.compose.testutils.expectError
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -81,7 +83,7 @@
 
         var calledCount = 0
         rule.onNodeWithTag(tag11)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(100, 50)) {
                 calledCount++
                 color11
@@ -89,17 +91,17 @@
         assertThat(calledCount).isEqualTo(100 * 50)
 
         rule.onNodeWithTag(tag12)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(100, 50)) {
                 color12
             }
         rule.onNodeWithTag(tag21)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(100, 50)) {
                 color21
             }
         rule.onNodeWithTag(tag22)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(100, 50)) {
                 color22
             }
@@ -110,7 +112,7 @@
         composeCheckerboard()
 
         rule.onNodeWithTag(rootTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(200, 100)) {
                 if (it.y >= 100 || it.x >= 200) {
                     throw AssertionError("$it is out of range!")
@@ -124,7 +126,7 @@
         composeCheckerboard()
 
         rule.onNodeWithTag(tag11)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(100, 50)) {
                 color22 // Assuming wrong color
             }
@@ -135,7 +137,7 @@
         composeCheckerboard()
 
         rule.onNodeWithTag(tag11)
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedSize = IntSize(10, 10)) {
                 color21
             }
@@ -150,7 +152,7 @@
         }
 
         rule.onNode(isDialog())
-            .captureToBitmap()
+            .captureToImage()
             .assertContainsColor(Color.Red)
     }
 
@@ -169,7 +171,7 @@
             expectedMessage = ".*Popups currently cannot be captured to bitmap.*"
         ) {
             rule.onNode(isPopup())
-                .captureToBitmap()
+                .captureToImage()
         }
     }
 
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
index 2dca73b..6e27460 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
@@ -25,11 +25,13 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageAsset
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.addOutline
 import androidx.compose.ui.graphics.asAndroidPath
+import androidx.compose.ui.graphics.asImageAsset
 import androidx.compose.ui.node.ExperimentalLayoutNodeApi
 import androidx.compose.ui.platform.AndroidOwner
 import androidx.compose.ui.semantics.SemanticsProperties
@@ -44,6 +46,19 @@
 import kotlin.math.roundToInt
 
 /**
+ * Captures the underlying semantics node's surface into an image.
+ *
+ * This has a limitation that if there is another window covering part of this node, such a
+ * window won't occur in this image.
+ *
+ * @throws IllegalArgumentException if an image is taken inside of a popup.
+ */
+@Suppress("DEPRECATION")
+@RequiresApi(Build.VERSION_CODES.O)
+fun SemanticsNodeInteraction.captureToImage(): ImageAsset =
+    captureToBitmap().asImageAsset()
+
+/**
  * Captures the underlying semantics node's surface into bitmap.
  *
  * This has a limitation that if there is another window covering part of this node, such a
@@ -51,6 +66,10 @@
  *
  * @throws IllegalArgumentException if a bitmap is taken inside of a popup.
 */
+@Deprecated(
+    "Replaced with captureToImage()",
+    replaceWith = ReplaceWith("captureToImage()")
+)
 @RequiresApi(Build.VERSION_CODES.O)
 fun SemanticsNodeInteraction.captureToBitmap(): Bitmap {
     val node = fetchSemanticsNode("Failed to capture a node to bitmap.")
@@ -121,12 +140,27 @@
 }
 
 /**
+ * Captures the underlying view's surface into an image.
+ *
+ * This has currently several limitations. Currently we assume that the view is hosted in
+ * Activity's window. Also if there is another window covering part of the component if won't occur
+ * in the image as this is taken from the component's window surface.
+ */
+@Suppress("DEPRECATION")
+@RequiresApi(Build.VERSION_CODES.O)
+fun View.captureToImage(): ImageAsset = captureToBitmap().asImageAsset()
+
+/**
  * Captures the underlying view's surface into bitmap.
  *
  * This has currently several limitations. Currently we assume that the view is hosted in
  * Activity's window. Also if there is another window covering part of the component if won't occur
  * in the bitmap as this is taken from the component's window surface.
  */
+@Deprecated(
+    "Replaced with captureToImage()",
+    replaceWith = ReplaceWith("captureToImage()")
+)
 @RequiresApi(Build.VERSION_CODES.O)
 fun View.captureToBitmap(): Bitmap {
     val locationInWindow = intArrayOf(0, 0)
@@ -145,7 +179,10 @@
  * The returned color is then asserted as the expected one on the given bitmap.
  *
  * @throws AssertionError if size or colors don't match.
+ * @Deprecated This API is going to be removed entirely
  */
+@Suppress("DEPRECATION")
+@Deprecated("This API is going to be removed entirely.")
 fun Bitmap.assertPixels(
     expectedSize: IntSize? = null,
     expectedColorProvider: (pos: IntOffset) -> Color?
@@ -172,7 +209,10 @@
 
 /**
  * Asserts that the color at a specific pixel in the bitmap at ([x], [y]) is [expected].
+ *
+ * @Deprecated This API is going to be removed entirely
  */
+@Deprecated("This API is going to be removed entirely.")
 fun Bitmap.assertPixelColor(
     expected: Color,
     x: Int,
@@ -191,7 +231,9 @@
  * Asserts that the expected color is present in this bitmap.
  *
  * @throws AssertionError if the expected color is not present.
+ * @Deprecated This API is going to be removed entirely
  */
+@Deprecated("This API is going to be removed entirely.")
 fun Bitmap.assertContainsColor(
     expectedColor: Color
 ): Bitmap {
@@ -224,7 +266,10 @@
  * The `point` argument is interpreted as an offset from the origin.
  *
  * Returns true if the point is in the path, and false otherwise.
+ *
+ * @Deprecated This API is going to be removed entirely
  */
+@Deprecated("This API is going to be removed entirely.")
 fun Path.contains(offset: Offset): Boolean {
     val path = android.graphics.Path()
     path.addRect(
@@ -259,8 +304,12 @@
  * @param centerY the Y position of the center of the [shape] inside the [sizeY]
  * @param shapeOverlapPixelCount The size of the border area from the shape outline to leave it
  * untested as it is likely anti-aliased. The default is 1 pixel
+ *
+ * @Deprecated This API is going to be removed entirely
  */
 // TODO (mount, malkov) : to investigate why it flakes when shape is not rect
+@Suppress("DEPRECATION")
+@Deprecated("This API is going to be removed entirely.")
 fun Bitmap.assertShape(
     density: Density,
     shape: Shape,
@@ -343,7 +392,11 @@
  * @param shape defines the [Shape]
  * @param shapeOverlapPixelCount The size of the border area from the shape outline to leave it
  * untested as it is likely anti-aliased. The default is 1 pixel
+ *
+ * @Deprecated This API is going to be removed entirely
  */
+@Suppress("DEPRECATION")
+@Deprecated("This API is going to be removed entirely.")
 fun Bitmap.assertShape(
     density: Density,
     horizontalPadding: Dp,
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/ui/test/CaptureToBitmapStubs.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/ui/test/CaptureToBitmapStubs.kt
index f482924..0be5489 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/ui/test/CaptureToBitmapStubs.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/ui/test/CaptureToBitmapStubs.kt
@@ -18,8 +18,9 @@
 
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 
 /**
  * @Deprecated Moved to androidx.compose.ui.test in androidx.compose.ui:ui-test library
@@ -30,4 +31,4 @@
     replaceWith = ReplaceWith("captureToBitmap()", "androidx.compose.ui.test")
 )
 @RequiresApi(Build.VERSION_CODES.O)
-fun SemanticsNodeInteraction.captureToBitmap() = captureToBitmap()
+fun SemanticsNodeInteraction.captureToBitmap() = captureToImage().asAndroidBitmap()
diff --git a/compose/ui/ui-text-android/api/current.txt b/compose/ui/ui-text-android/api/current.txt
deleted file mode 100644
index a8713bb..0000000
--- a/compose/ui/ui-text-android/api/current.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-// Signature format: 4.0
-package androidx.compose.ui.text.android {
-
-  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
-  }
-
-  public final class LayoutCompatKt {
-  }
-
-  public final class LayoutHelperKt {
-  }
-
-  public final class LayoutIntrinsicsKt {
-  }
-
-  public final class TextLayoutKt {
-  }
-
-}
-
-package androidx.compose.ui.text.android.style {
-
-  public final class FontWeightStyleSpanKt {
-  }
-
-  public final class PlaceholderSpanKt {
-  }
-
-}
-
diff --git a/compose/ui/ui-text-android/api/public_plus_experimental_current.txt b/compose/ui/ui-text-android/api/public_plus_experimental_current.txt
deleted file mode 100644
index a8713bb..0000000
--- a/compose/ui/ui-text-android/api/public_plus_experimental_current.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-// Signature format: 4.0
-package androidx.compose.ui.text.android {
-
-  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
-  }
-
-  public final class LayoutCompatKt {
-  }
-
-  public final class LayoutHelperKt {
-  }
-
-  public final class LayoutIntrinsicsKt {
-  }
-
-  public final class TextLayoutKt {
-  }
-
-}
-
-package androidx.compose.ui.text.android.style {
-
-  public final class FontWeightStyleSpanKt {
-  }
-
-  public final class PlaceholderSpanKt {
-  }
-
-}
-
diff --git a/compose/ui/ui-text-android/api/res-current.txt b/compose/ui/ui-text-android/api/res-current.txt
deleted file mode 100644
index e69de29..0000000
--- a/compose/ui/ui-text-android/api/res-current.txt
+++ /dev/null
diff --git a/compose/ui/ui-text-android/api/restricted_current.txt b/compose/ui/ui-text-android/api/restricted_current.txt
deleted file mode 100644
index a8713bb..0000000
--- a/compose/ui/ui-text-android/api/restricted_current.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-// Signature format: 4.0
-package androidx.compose.ui.text.android {
-
-  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
-  }
-
-  public final class LayoutCompatKt {
-  }
-
-  public final class LayoutHelperKt {
-  }
-
-  public final class LayoutIntrinsicsKt {
-  }
-
-  public final class TextLayoutKt {
-  }
-
-}
-
-package androidx.compose.ui.text.android.style {
-
-  public final class FontWeightStyleSpanKt {
-  }
-
-  public final class PlaceholderSpanKt {
-  }
-
-}
-
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 68796a6..0276f50 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -525,6 +525,35 @@
 
 }
 
+package androidx.compose.ui.text.android {
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
+  }
+
+  public final class LayoutCompatKt {
+  }
+
+  public final class LayoutHelperKt {
+  }
+
+  public final class LayoutIntrinsicsKt {
+  }
+
+  public final class TextLayoutKt {
+  }
+
+}
+
+package androidx.compose.ui.text.android.style {
+
+  public final class FontWeightStyleSpanKt {
+  }
+
+  public final class PlaceholderSpanKt {
+  }
+
+}
+
 package androidx.compose.ui.text.font {
 
   public abstract sealed class FileBasedFontFamily extends androidx.compose.ui.text.font.FontFamily {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 68796a6..0276f50 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -525,6 +525,35 @@
 
 }
 
+package androidx.compose.ui.text.android {
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
+  }
+
+  public final class LayoutCompatKt {
+  }
+
+  public final class LayoutHelperKt {
+  }
+
+  public final class LayoutIntrinsicsKt {
+  }
+
+  public final class TextLayoutKt {
+  }
+
+}
+
+package androidx.compose.ui.text.android.style {
+
+  public final class FontWeightStyleSpanKt {
+  }
+
+  public final class PlaceholderSpanKt {
+  }
+
+}
+
 package androidx.compose.ui.text.font {
 
   public abstract sealed class FileBasedFontFamily extends androidx.compose.ui.text.font.FontFamily {
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 68796a6..0276f50 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -525,6 +525,35 @@
 
 }
 
+package androidx.compose.ui.text.android {
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalPlatformTextApi {
+  }
+
+  public final class LayoutCompatKt {
+  }
+
+  public final class LayoutHelperKt {
+  }
+
+  public final class LayoutIntrinsicsKt {
+  }
+
+  public final class TextLayoutKt {
+  }
+
+}
+
+package androidx.compose.ui.text.android.style {
+
+  public final class FontWeightStyleSpanKt {
+  }
+
+  public final class PlaceholderSpanKt {
+  }
+
+}
+
 package androidx.compose.ui.text.font {
 
   public abstract sealed class FileBasedFontFamily extends androidx.compose.ui.text.font.FontFamily {
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index c230029..b40e26e 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -49,7 +49,6 @@
         implementation(KOTLIN_STDLIB)
         implementation "androidx.collection:collection:1.1.0"
         implementation "androidx.core:core:1.5.0-alpha01"
-        implementation project(":compose:ui:ui-text-android")
 
         testImplementation project(":compose:ui:ui-test-font")
         testImplementation(ANDROIDX_TEST_RULES)
@@ -75,7 +74,14 @@
         androidTestImplementation MOCKITO_KOTLIN, {
             exclude group: 'org.mockito' // to keep control on the mockito version
         }
+    }
 
+    android {
+        sourceSets {
+            main {
+                java.srcDirs += "../../../text/text/src/main/java"
+            }
+        }
     }
 }
 
@@ -110,9 +116,10 @@
                 api "androidx.annotation:annotation:1.1.0"
                 implementation "androidx.collection:collection:1.1.0"
                 implementation "androidx.core:core:1.5.0-alpha01"
-                implementation project(":compose:ui:ui-text-android")
             }
 
+            androidMain.kotlin.srcDirs("../../../text/text/src/main/java")
+
             androidTest.dependencies {
                 implementation project(":compose:ui:ui-test-font")
                 implementation(ANDROIDX_TEST_RULES)
diff --git a/compose/ui/ui-text/lint-baseline.xml b/compose/ui/ui-text/lint-baseline.xml
index 53bc744..27327af 100644
--- a/compose/ui/ui-text/lint-baseline.xml
+++ b/compose/ui/ui-text/lint-baseline.xml
@@ -78,4 +78,213 @@
             column="22"/>
     </issue>
 
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.style.FontSpan is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="            oldTypeface.weight"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/style/FontSpan.kt"
+            line="46"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.style.FontWeightStyleSpan is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="            (weight != 0 &amp;&amp; weight != oldTypeface?.weight)"
+        errorLine2="                                                   ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt"
+            line="67"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.style.FontWeightStyleSpan is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="            oldTypeface?.weight ?: FontStyle.FONT_WEIGHT_NORMAL"
+        errorLine2="                         ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt"
+            line="79"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.style.FontWeightStyleSpan is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="        textPaint.typeface = Typeface.create(oldTypeface, newWeight, newItalic)"
+        errorLine2="                                      ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt"
+            line="88"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.style.FontWeightStyleSpan is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="        textPaint.typeface = Typeface.create(oldTypeface, newWeight, newItalic)"
+        errorLine2="                                      ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt"
+            line="88"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="            Builder.obtain(text, start, end, paint, width)"
+        errorLine2="                    ~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="108"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setTextDirection(textDir)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="110"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setAlignment(alignment)"
+        errorLine2="                    ~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="111"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setMaxLines(maxLines)"
+        errorLine2="                    ~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="112"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setEllipsize(ellipsize)"
+        errorLine2="                    ~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="113"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setEllipsizedWidth(ellipsizedWidth)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="114"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)"
+        errorLine2="                    ~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="115"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        errorLine1="                        setJustificationMode(justificationMode)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="117"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setIncludePad(includePadding)"
+        errorLine2="                    ~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="119"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 28, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 28. Either annotate the containing class with at least @RequiresApi(28) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(28)."
+        errorLine1="                        setUseLineSpacingFromFallbacks(fallbackLineSpacing)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="121"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setBreakStrategy(breakStrategy)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="123"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setHyphenationFrequency(hyphenationFrequency)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="124"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                    setIndents(leftIndents, rightIndents)"
+        errorLine2="                    ~~~~~~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="125"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="UnsafeNewApiCall"
+        message="This call is to a method from API 23, the call containing class androidx.compose.ui.text.android.StaticLayoutFactory is not annotated with @RequiresApi(x) where x is at least 23. Either annotate the containing class with at least @RequiresApi(23) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(23)."
+        errorLine1="                }.build()"
+        errorLine2="                  ~~~~~">
+        <location
+            file="../../../text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt"
+            line="126"
+            column="19"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
index f4777fa..5564361 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
@@ -45,9 +45,12 @@
     val capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
     val autoCorrect: Boolean = true,
     val keyboardType: KeyboardType = KeyboardType.Text,
-    val imeAction: ImeAction = ImeAction.Unspecified,
+    val imeAction: ImeAction = ImeAction.Unspecified
 ) {
     companion object {
+        /**
+         * Default [ImeOptions]. Please see parameter descriptions for default values.
+         */
         val Default = ImeOptions()
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-viewbinding/build.gradle b/compose/ui/ui-viewbinding/build.gradle
index 1ab1a3c..ed88e8c 100644
--- a/compose/ui/ui-viewbinding/build.gradle
+++ b/compose/ui/ui-viewbinding/build.gradle
@@ -35,6 +35,7 @@
     implementation project(":compose:ui:ui")
 
     androidTestImplementation project(':compose:foundation:foundation')
+    androidTestImplementation project(":compose:test-utils")
     androidTestImplementation project(':ui:ui-test')
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(JUNIT)
diff --git a/compose/ui/ui-viewbinding/src/androidTest/java/androidx/compose/ui/viewinterop/AndroidViewBindingTest.kt b/compose/ui/ui-viewbinding/src/androidTest/java/androidx/compose/ui/viewinterop/AndroidViewBindingTest.kt
index 1521d22..7686fd2 100644
--- a/compose/ui/ui-viewbinding/src/androidTest/java/androidx/compose/ui/viewinterop/AndroidViewBindingTest.kt
+++ b/compose/ui/ui-viewbinding/src/androidTest/java/androidx/compose/ui/viewinterop/AndroidViewBindingTest.kt
@@ -20,14 +20,14 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -59,7 +59,7 @@
 
         val size = 50.dp
         val sizePx = with(rule.density) { size.toIntPx() }
-        rule.onNodeWithTag("layout").captureToBitmap().assertPixels(IntSize(sizePx, sizePx * 2)) {
+        rule.onNodeWithTag("layout").captureToImage().assertPixels(IntSize(sizePx, sizePx * 2)) {
             if (it.y < sizePx) Color.Blue else Color.Black
         }
     }
@@ -76,14 +76,16 @@
 
         val size = 50.dp
         val sizePx = with(rule.density) { size.toIntPx() }
-        rule.onNodeWithTag("layout").captureToBitmap().assertPixels(IntSize(sizePx, sizePx * 2)) {
-            if (it.y < sizePx) Color.Blue else color.value
-        }
+        rule.onNodeWithTag("layout").captureToImage()
+            .assertPixels(IntSize(sizePx, sizePx * 2)) {
+                if (it.y < sizePx) Color.Blue else color.value
+            }
 
         rule.runOnIdle { color.value = Color.DarkGray }
-        rule.onNodeWithTag("layout").captureToBitmap().assertPixels(IntSize(sizePx, sizePx * 2)) {
-            if (it.y < sizePx) Color.Blue else color.value
-        }
+        rule.onNodeWithTag("layout").captureToImage()
+            .assertPixels(IntSize(sizePx, sizePx * 2)) {
+                if (it.y < sizePx) Color.Blue else color.value
+            }
     }
 
     @Test
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 62005ed..63fd054 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1591,18 +1591,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface ContentScale {
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+    method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
     field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
   }
 
   public static final class ContentScale.Companion {
     method public androidx.compose.ui.layout.ContentScale getCrop();
+    method public androidx.compose.ui.layout.ContentScale getFillBounds();
     method public androidx.compose.ui.layout.ContentScale getFillHeight();
     method public androidx.compose.ui.layout.ContentScale getFillWidth();
     method public androidx.compose.ui.layout.ContentScale getFit();
     method public androidx.compose.ui.layout.ContentScale getInside();
     method public androidx.compose.ui.layout.FixedScale getNone();
     property public final androidx.compose.ui.layout.ContentScale Crop;
+    property public final androidx.compose.ui.layout.ContentScale FillBounds;
     property public final androidx.compose.ui.layout.ContentScale FillHeight;
     property public final androidx.compose.ui.layout.ContentScale FillWidth;
     property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1616,9 +1619,9 @@
   @androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
     ctor public FixedScale(float value);
     method public float component1();
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
     method public float getValue();
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
     property public final float value;
   }
 
@@ -1800,6 +1803,36 @@
     method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
   }
 
+  @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+    ctor public ScaleFactor();
+    method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+    method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+    method public static long constructor-impl(internal long packedValue);
+    method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static float getScaleX-impl(long $this);
+    method public static float getScaleY-impl(long $this);
+    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+    method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+    method public static String toString-impl(long $this);
+    field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+  }
+
+  public static final class ScaleFactor.Companion {
+    method public long getUnspecified-_hLwfpc();
+    property public final long Unspecified;
+  }
+
+  public final class ScaleFactorKt {
+    method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+    method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+    method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+  }
+
   public final class SubcomposeLayoutKt {
     method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
   }
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 62005ed..63fd054 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1591,18 +1591,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface ContentScale {
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+    method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
     field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
   }
 
   public static final class ContentScale.Companion {
     method public androidx.compose.ui.layout.ContentScale getCrop();
+    method public androidx.compose.ui.layout.ContentScale getFillBounds();
     method public androidx.compose.ui.layout.ContentScale getFillHeight();
     method public androidx.compose.ui.layout.ContentScale getFillWidth();
     method public androidx.compose.ui.layout.ContentScale getFit();
     method public androidx.compose.ui.layout.ContentScale getInside();
     method public androidx.compose.ui.layout.FixedScale getNone();
     property public final androidx.compose.ui.layout.ContentScale Crop;
+    property public final androidx.compose.ui.layout.ContentScale FillBounds;
     property public final androidx.compose.ui.layout.ContentScale FillHeight;
     property public final androidx.compose.ui.layout.ContentScale FillWidth;
     property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1616,9 +1619,9 @@
   @androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
     ctor public FixedScale(float value);
     method public float component1();
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
     method public float getValue();
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
     property public final float value;
   }
 
@@ -1800,6 +1803,36 @@
     method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
   }
 
+  @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+    ctor public ScaleFactor();
+    method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+    method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+    method public static long constructor-impl(internal long packedValue);
+    method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static float getScaleX-impl(long $this);
+    method public static float getScaleY-impl(long $this);
+    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+    method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+    method public static String toString-impl(long $this);
+    field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+  }
+
+  public static final class ScaleFactor.Companion {
+    method public long getUnspecified-_hLwfpc();
+    property public final long Unspecified;
+  }
+
+  public final class ScaleFactorKt {
+    method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+    method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+    method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+  }
+
   public final class SubcomposeLayoutKt {
     method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
   }
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index eee4efa..a7df2e5 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1591,18 +1591,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface ContentScale {
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+    method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
     field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
   }
 
   public static final class ContentScale.Companion {
     method public androidx.compose.ui.layout.ContentScale getCrop();
+    method public androidx.compose.ui.layout.ContentScale getFillBounds();
     method public androidx.compose.ui.layout.ContentScale getFillHeight();
     method public androidx.compose.ui.layout.ContentScale getFillWidth();
     method public androidx.compose.ui.layout.ContentScale getFit();
     method public androidx.compose.ui.layout.ContentScale getInside();
     method public androidx.compose.ui.layout.FixedScale getNone();
     property public final androidx.compose.ui.layout.ContentScale Crop;
+    property public final androidx.compose.ui.layout.ContentScale FillBounds;
     property public final androidx.compose.ui.layout.ContentScale FillHeight;
     property public final androidx.compose.ui.layout.ContentScale FillWidth;
     property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1633,9 +1636,9 @@
   @androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
     ctor public FixedScale(float value);
     method public float component1();
+    method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
     method public float getValue();
-    method public float scale-AhF4CD4(long srcSize, long dstSize);
     property public final float value;
   }
 
@@ -1847,6 +1850,36 @@
     method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
   }
 
+  @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+    ctor public ScaleFactor();
+    method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+    method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+    method public static long constructor-impl(internal long packedValue);
+    method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static float getScaleX-impl(long $this);
+    method public static float getScaleY-impl(long $this);
+    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+    method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+    method public static String toString-impl(long $this);
+    field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+  }
+
+  public static final class ScaleFactor.Companion {
+    method public long getUnspecified-_hLwfpc();
+    property public final long Unspecified;
+  }
+
+  public final class ScaleFactorKt {
+    method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+    method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+    method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+    method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+  }
+
   public final class SubcomposeLayoutKt {
     method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
   }
diff --git a/compose/ui/ui/lint-baseline.xml b/compose/ui/ui/lint-baseline.xml
index 1ce1838..6601660 100644
--- a/compose/ui/ui/lint-baseline.xml
+++ b/compose/ui/ui/lint-baseline.xml
@@ -290,72 +290,6 @@
     <issue
         id="ModifierInspectorInfo"
         message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilter.kt"
-            line="63"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/DragGestureFilter.kt"
-            line="61"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilter.kt"
-            line="65"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1=") = this.then(DrawBackgroundModifier(onDraw))"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt"
-            line="58"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1=") = composed {"
-        errorLine2="    ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt"
-            line="86"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
-        errorLine1="): Modifier = this.then(object : DrawModifier {"
-        errorLine2="                        ^">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt"
-            line="182"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifiers should include inspectorInfo for the Layout Inspector"
         errorLine1=") = if (elevation > 0.dp || clip) {"
         errorLine2="    ^">
         <location
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index d76236c..054014a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -47,12 +47,14 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.asImageAsset
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.drawscope.clipRect
 import androidx.compose.ui.graphics.drawscope.translate
@@ -87,7 +89,6 @@
 import androidx.compose.ui.platform.ViewLayerContainer
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.TestActivity
-import androidx.compose.ui.test.assertPixels
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
@@ -3179,7 +3180,7 @@
             )
         }
 
-        activityTestRule.waitAndScreenShot(frameLayout)
+        activityTestRule.waitAndScreenShot(frameLayout).asImageAsset()
             .assertPixels(expectedSize = IntSize(size, size)) {
                 Color.Red
             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawLayerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawLayerTest.kt
index ed5c78c..52f5464 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawLayerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawLayerTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.FixedSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Padding
@@ -33,6 +34,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.boundsInRoot
@@ -42,8 +44,7 @@
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.TestActivity
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
@@ -236,7 +237,7 @@
             }
         }
 
-        rule.onNodeWithTag(testTag).captureToBitmap().apply {
+        rule.onNodeWithTag(testTag).captureToImage().asAndroidBitmap().apply {
             assertEquals(Color.Red.toArgb(), getPixel(0, 0))
             assertEquals(Color.Red.toArgb(), getPixel(0, height - 1))
             assertEquals(Color.Red.toArgb(), getPixel(width / 2 - 10, height / 2))
@@ -268,7 +269,7 @@
 
         // Results should match background color of parent. Because the child Box is clipped to
         // an empty rectangle, no red pixels from its background should be visible
-        rule.onNodeWithTag(tag).captureToBitmap().assertPixels { Color.Blue }
+        rule.onNodeWithTag(tag).captureToImage().assertPixels { Color.Blue }
     }
 
     @Test
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
index 140cbf5..b4496f4 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
@@ -24,22 +24,34 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.AtLeastSize
+import androidx.compose.ui.CacheDrawScope
+import androidx.compose.ui.DrawResult
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.drawBehind
 import androidx.compose.ui.drawWithCache
+import androidx.compose.ui.drawWithContent
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performClick
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,6 +63,16 @@
     @get:Rule
     val rule = createComposeRule()
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
     fun testCacheHitWithStateChange() {
@@ -341,4 +363,42 @@
             }
         }
     }
+
+    @Test
+    fun testInspectorValueForDrawBehind() {
+        val onDraw: DrawScope.() -> Unit = {}
+        rule.setContent {
+            val modifier = Modifier.drawBehind(onDraw) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("drawBehind")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.map { it.name }.asIterable())
+                .containsExactly("onDraw")
+        }
+    }
+
+    @Test
+    fun testInspectorValueForDrawWithCache() {
+        val onBuildDrawCache: CacheDrawScope.() -> DrawResult = { DrawResult {} }
+        rule.setContent {
+            val modifier = Modifier.drawWithCache(onBuildDrawCache) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("drawWithCache")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.map { it.name }.asIterable())
+                .containsExactly("onBuildDrawCache")
+        }
+    }
+
+    @Test
+    fun testInspectorValueForDrawWithContent() {
+        val onDraw: DrawScope.() -> Unit = {}
+        rule.setContent {
+            val modifier = Modifier.drawWithContent(onDraw) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("drawWithContent")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.map { it.name }.asIterable())
+                .containsExactly("onDraw")
+        }
+    }
+
+    fun SemanticsNodeInteraction.captureToBitmap() = captureToImage().asAndroidBitmap()
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/InvalidatingNotPlacedChildTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/InvalidatingNotPlacedChildTest.kt
index 368cdaa..fc69171 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/InvalidatingNotPlacedChildTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/InvalidatingNotPlacedChildTest.kt
@@ -32,7 +32,7 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.assertCenterPixelColor
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.dp
@@ -69,7 +69,7 @@
         }
 
         composeTestRule.onNodeWithTag("node")
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Blue)
 
         composeTestRule.runOnIdle {
@@ -77,7 +77,7 @@
         }
 
         composeTestRule.onNodeWithTag("node")
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Red)
     }
 
@@ -102,7 +102,7 @@
         }
 
         composeTestRule.onNodeWithTag("node")
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Blue)
 
         composeTestRule.runOnIdle {
@@ -110,7 +110,7 @@
         }
 
         composeTestRule.onNodeWithTag("node")
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Gray)
     }
 
@@ -148,7 +148,7 @@
         }
 
         composeTestRule.onNodeWithTag("node")
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Red)
     }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
index 86f21bd..b5ef674 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
@@ -26,6 +26,7 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.AlignTopLeft
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.AtLeastSize
@@ -43,6 +44,8 @@
 import androidx.compose.ui.graphics.ImageAsset
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.asImageAsset
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.painter.ImagePainter
@@ -65,7 +68,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.ComposeTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
@@ -564,6 +567,35 @@
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
+    fun testImagePainterScalesNonUniformly() {
+        // The composable dimensions are larger than the ImageAsset. By not passing in
+        // a ContentScale parameter to the painter, the ImageAsset should be stretched
+        // non-uniformly to fully occupy the bounds of the composable
+        val boxWidth = 60
+        val boxHeight = 40
+        val srcImage = ImageAsset(10, 20)
+        val canvas = Canvas(srcImage)
+        val paint = Paint().apply { this.color = Color.Red }
+        canvas.drawRect(0f, 0f, 40f, 20f, paint)
+
+        val testTag = "testTag"
+
+        rule.setContent {
+            Box(
+                modifier = Modifier
+                    .testTag(testTag)
+                    .background(color = Color.Gray)
+                    .width((boxWidth / DensityAmbient.current.density).dp)
+                    .height((boxHeight / DensityAmbient.current.density).dp)
+                    .paint(ImagePainter(srcImage), contentScale = ContentScale.FillBounds)
+            )
+        }
+
+        rule.obtainScreenshotBitmap(boxWidth, boxHeight).asImageAsset().assertPixels { Color.Red }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
     fun testVectorPainterScalesContent() {
         // VectorPainter should handle scaling its content vector up to fill the
         // corresponding content bounds. Because the composable is twice the
@@ -660,10 +692,10 @@
 }
 
 private fun ComposeTestRule.obtainScreenshotBitmap(width: Int, height: Int = width): Bitmap {
-    val bitmap = onRoot().captureToBitmap()
+    val bitmap = onRoot().captureToImage()
     assertEquals(width, bitmap.width)
     assertEquals(height, bitmap.height)
-    return bitmap
+    return bitmap.asAndroidBitmap()
 }
 
 private class TestPainter(
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 8caa51b..4d47e25 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
@@ -27,6 +27,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.AtLeastSize
 import androidx.compose.ui.Modifier
@@ -35,11 +36,12 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
@@ -229,7 +231,7 @@
         }
 
         rule.onNodeWithTag(testTag).apply {
-            captureToBitmap().apply {
+            captureToImage().asAndroidBitmap().apply {
                 assertEquals(Color.Red.toArgb(), getPixel(width - 2, 0))
                 assertEquals(Color.Red.toArgb(), getPixel(2, 0))
                 assertEquals(Color.Red.toArgb(), getPixel(width - 1, height - 2))
@@ -243,7 +245,7 @@
 
         rule.waitForIdle()
 
-        rule.onNodeWithTag(testTag).captureToBitmap().apply {
+        rule.onNodeWithTag(testTag).captureToImage().asAndroidBitmap().apply {
             assertEquals(Color.Black.toArgb(), getPixel(width - 2, 0))
             assertEquals(Color.Black.toArgb(), getPixel(2, 0))
             assertEquals(Color.Black.toArgb(), getPixel(width - 1, height - 2))
@@ -254,6 +256,40 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testVectorScaleNonUniformly() {
+        val defaultWidth = 24.dp
+        val defaultHeight = 24.dp
+        val testTag = "testTag"
+        rule.setContent {
+            val vectorPainter = rememberVectorPainter(
+                defaultWidth = defaultWidth,
+                defaultHeight = defaultHeight
+            ) { viewportWidth, viewportHeight ->
+                Path(
+                    fill = SolidColor(Color.Blue),
+                    pathData = PathData {
+                        lineTo(viewportWidth, 0f)
+                        lineTo(viewportWidth, viewportHeight)
+                        lineTo(0f, viewportHeight)
+                        close()
+                    }
+                )
+            }
+            Image(
+                painter = vectorPainter,
+                modifier = Modifier
+                    .testTag(testTag)
+                    .preferredSize(defaultWidth * 7, defaultHeight * 3)
+                    .background(Color.Red),
+                contentScale = ContentScale.FillBounds
+            )
+        }
+
+        rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
+    }
+
     @Composable
     private fun VectorTint(
         size: Int = 200,
@@ -386,7 +422,7 @@
     }
 
     private fun takeScreenShot(width: Int, height: Int = width): Bitmap {
-        val bitmap = rule.onRoot().captureToBitmap()
+        val bitmap = rule.onRoot().captureToImage().asAndroidBitmap()
         Assert.assertEquals(width, bitmap.width)
         Assert.assertEquals(height, bitmap.height)
         return bitmap
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
index dd43281..db6a5de 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
@@ -32,11 +32,12 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.pressIndicatorGestureFilter
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -129,7 +130,7 @@
             await(5, TimeUnit.SECONDS)
         }
 
-        node.captureToBitmap().apply {
+        node.captureToImage().asAndroidBitmap().apply {
             Assert.assertEquals(
                 Color.Red.toArgb(),
                 getPixel(
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index 33efb52..da1aae6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.layout
 
-import android.graphics.Bitmap
 import android.os.Build
 import android.widget.FrameLayout
 import androidx.activity.ComponentActivity
@@ -33,6 +32,8 @@
 import androidx.compose.ui.background
 import androidx.compose.ui.draw.assertColor
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.setContent
@@ -41,7 +42,7 @@
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -347,7 +348,7 @@
         rule.waitForIdle()
 
         rule.onNodeWithTag(layoutTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Green)
     }
 
@@ -375,7 +376,7 @@
         }
 
         rule.onNodeWithTag(layoutTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Green)
 
         rule.runOnIdle {
@@ -383,7 +384,7 @@
         }
 
         rule.onNodeWithTag(layoutTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Red)
     }
 
@@ -408,7 +409,7 @@
         }
 
         rule.onNodeWithTag(layoutTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Red)
     }
 
@@ -492,7 +493,7 @@
         rule.waitForIdle()
 
         rule.onNodeWithTag(layoutTag)
-            .captureToBitmap()
+            .captureToImage()
             .assertCenterPixelColor(Color.Red)
     }
 
@@ -551,6 +552,6 @@
     }
 }
 
-fun Bitmap.assertCenterPixelColor(expectedColor: Color) {
-    assertColor(expectedColor, width / 2, height / 2)
+fun ImageAsset.assertCenterPixelColor(expectedColor: Color) {
+    asAndroidBitmap().assertColor(expectedColor, width / 2, height / 2)
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidViewCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidViewCompatTest.kt
index 7c8da96..678449d9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidViewCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidViewCompatTest.kt
@@ -43,6 +43,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Align
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.background
@@ -67,8 +68,7 @@
 import androidx.compose.ui.node.isAttached
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Constraints
@@ -198,7 +198,7 @@
         }
         rule.onNodeWithTag("content")
             .assertIsDisplayed()
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedColorProvider = expectedPixelColor)
 
         rule.runOnUiThread {
@@ -212,7 +212,7 @@
             .check(matches(`is`(squareView)))
         rule.onNodeWithTag("content")
             .assertIsDisplayed()
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedColorProvider = expectedPixelColor)
 
         rule.runOnUiThread {
@@ -226,7 +226,7 @@
             .check(matches(`is`(squareView)))
         rule.onNodeWithTag("content")
             .assertIsDisplayed()
-            .captureToBitmap()
+            .captureToImage()
             .assertPixels(expectedColorProvider = expectedPixelColor)
     }
 
@@ -418,15 +418,15 @@
                 view.setBackgroundColor(android.graphics.Color.BLUE)
             }
         }
-        rule.onNodeWithTag("view").captureToBitmap()
+        rule.onNodeWithTag("view").captureToImage()
             .assertPixels(IntSize(size, size)) { Color.Blue }
 
         rule.runOnIdle { size += 20 }
-        rule.onNodeWithTag("view").captureToBitmap()
+        rule.onNodeWithTag("view").captureToImage()
             .assertPixels(IntSize(size, size)) { Color.Blue }
 
         rule.runOnIdle { size += 20 }
-        rule.onNodeWithTag("view").captureToBitmap()
+        rule.onNodeWithTag("view").captureToImage()
             .assertPixels(IntSize(size, size)) { Color.Blue }
     }
 
@@ -558,7 +558,7 @@
             }
         }
 
-        rule.onNodeWithTag("box").captureToBitmap().assertPixels(
+        rule.onNodeWithTag("box").captureToImage().assertPixels(
             IntSize((padding * 2 + size * 2).roundToInt(), (padding * 2 + size).roundToInt())
         ) { offset ->
             if (offset.y < padding || offset.y >= padding + size || offset.x < padding ||
@@ -654,12 +654,12 @@
         }
 
         rule.onNodeWithTag("view")
-            .captureToBitmap().assertPixels(IntSize(sizePx, sizePx)) { Color.Green }
+            .captureToImage().assertPixels(IntSize(sizePx, sizePx)) { Color.Green }
 
         rule.runOnIdle { first = false }
 
         rule.onNodeWithTag("view")
-            .captureToBitmap().assertPixels(IntSize(sizePx, sizePx)) { Color.Blue }
+            .captureToImage().assertPixels(IntSize(sizePx, sizePx)) { Color.Blue }
     }
 
     @Test
@@ -692,14 +692,14 @@
             }
         }
 
-        rule.onNodeWithTag("view").captureToBitmap()
+        rule.onNodeWithTag("view").captureToImage()
             .assertPixels(IntSize(sizePx * 2, sizePx)) {
                 if (it.x < sizePx) Color.Green else Color.Blue
             }
 
         rule.runOnIdle { first = false }
 
-        rule.onNodeWithTag("view").captureToBitmap()
+        rule.onNodeWithTag("view").captureToImage()
             .assertPixels(IntSize(sizePx * 2, sizePx)) {
                 if (it.x < sizePx) Color.Blue else Color.Green
             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
index 0257cab..89929be 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.onGloballyPositioned
@@ -43,8 +44,7 @@
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.R
-import androidx.compose.ui.test.assertPixels
-import androidx.compose.ui.test.captureToBitmap
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
@@ -250,7 +250,7 @@
             AndroidView({ frameLayout }, Modifier.testTag("view").background(color = Color.Blue))
         }
 
-        rule.onNodeWithTag("view").captureToBitmap().assertPixels(IntSize(size, size)) {
+        rule.onNodeWithTag("view").captureToImage().assertPixels(IntSize(size, size)) {
             Color.Blue
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt
index 087a820..3ae3b46 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/DrawModifier.kt
@@ -20,6 +20,9 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Density
 
 /**
@@ -55,11 +58,20 @@
  */
 fun Modifier.drawBehind(
     onDraw: DrawScope.() -> Unit
-) = this.then(DrawBackgroundModifier(onDraw))
+) = this.then(
+    DrawBackgroundModifier(
+        >
+        inspectorInfo = debugInspectorInfo {
+            name = "drawBehind"
+            properties["onDraw"] = onDraw
+        }
+    )
+)
 
 private class DrawBackgroundModifier(
-    val onDraw: DrawScope.() -> Unit
-) : DrawModifier {
+    val onDraw: DrawScope.() -> Unit,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : DrawModifier, InspectorValueInfo(inspectorInfo) {
     override fun ContentDrawScope.draw() {
         onDraw()
         drawContent()
@@ -83,7 +95,12 @@
  */
 fun Modifier.drawWithCache(
     onBuildDrawCache: CacheDrawScope.() -> DrawResult
-) = composed {
+) = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "drawWithCache"
+        properties["onBuildDrawCache"] = onBuildDrawCache
+    }
+) {
     val cacheDrawScope = remember { CacheDrawScope() }
     this.then(DrawContentCacheModifier(cacheDrawScope, onBuildDrawCache))
 }
@@ -179,8 +196,15 @@
 // TODO: Inline this function -- it breaks with current compiler
 /*inline*/ fun Modifier.drawWithContent(
     onDraw: ContentDrawScope.() -> Unit
-): Modifier = this.then(object : DrawModifier {
-    override fun ContentDrawScope.draw() {
-        onDraw()
+): Modifier = this.then(
+    object : DrawModifier, InspectorValueInfo(
+        debugInspectorInfo {
+            name = "drawWithContent"
+            properties["onDraw"] = onDraw
+        }
+    ) {
+        override fun ContentDrawScope.draw() {
+            onDraw()
+        }
     }
-})
+)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
index b609dd9..2993b7f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
@@ -27,6 +27,7 @@
 import androidx.compose.ui.graphics.drawscope.translate
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.layout.times
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.LayoutModifier
@@ -183,7 +184,7 @@
             }
 
             val srcSize = Size(srcWidth, srcHeight)
-            srcSize * contentScale.scale(srcSize, dstSize)
+            srcSize * contentScale.computeScaleFactor(srcSize, dstSize)
         }
     }
 
@@ -246,7 +247,7 @@
         }
 
         val srcSize = Size(srcWidth, srcHeight)
-        val scale = contentScale.scale(srcSize, size)
+        val scale = contentScale.computeScaleFactor(srcSize, size)
 
         // Compute the offset to translate the content based on the given alignment
         // and size to draw based on the ContentScale parameter
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilter.kt
index a9f85d9..d70deea 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilter.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.input.pointer.changedToDown
 import androidx.compose.ui.input.pointer.changedToUp
 import androidx.compose.ui.input.pointer.consumeDownChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.inMilliseconds
 import androidx.compose.ui.util.annotation.VisibleForTesting
@@ -60,7 +61,12 @@
  */
 fun Modifier.doubleTapGestureFilter(
     onDoubleTap: (Offset) -> Unit
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "doubleTapGestureFilter"
+        value = onDoubleTap
+    }
+) {
     val scope = rememberCoroutineScope()
     val filter = remember { DoubleTapGestureFilter(scope) }
     filter.>
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragGestureFilter.kt
index 5eb5cdc..2066d92 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragGestureFilter.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.platform.debugInspectorInfo
 
 // TODO(b/146133703): Likely rename to PanGestureDetector as per b/146133703
 /**
@@ -58,7 +59,14 @@
     dragObserver: DragObserver,
     canDrag: ((Direction) -> Boolean)? = null,
     startDragImmediately: Boolean = false
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "dragGestureFilter"
+        properties["dragObserver"] = dragObserver
+        properties["canDrag"] = canDrag
+        properties["startDragImmediately"] = startDragImmediately
+    }
+) {
     val glue = remember { TouchSlopDragGestureDetectorGlue() }
     glue.touchSlopDragObserver = dragObserver
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilter.kt
index a3c1558..58d8b46 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilter.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import kotlin.math.abs
 
@@ -62,7 +63,14 @@
     onDragSlopExceeded: () -> Unit,
     canDrag: ((Direction) -> Boolean)? = null,
     orientation: Orientation? = null
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "dragSlopExceededGestureFilter"
+        properties["onDragSlopExceeded"] = onDragSlopExceeded
+        properties["canDrag"] = canDrag
+        properties["orientation"] = orientation
+    }
+) {
     val touchSlop = with(DensityAmbient.current) { TouchSlop.toPx() }
     val filter = remember {
         DragSlopExceededGestureFilter(touchSlop)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
index cbf29a5..7ecf32c3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
@@ -22,8 +22,6 @@
 import kotlin.math.max
 import kotlin.math.min
 
-private const val OriginalScale = 1.0f
-
 /**
  * Represents a rule to apply to scale a source rectangle to be inscribed into a destination
  */
@@ -31,10 +29,26 @@
 interface ContentScale {
 
     /**
+     * Computes the scale factor to apply to the horizontal and vertical axes independently
+     * of one another to fit the source appropriately with the given destination
+     */
+    fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor
+
+    /**
      * Computes the scale factor to apply to both dimensions in order to fit the source
      * appropriately with the given destination size
      */
-    fun scale(srcSize: Size, dstSize: Size): Float
+    @Deprecated(
+        "use computeScaleFactor instead",
+        ReplaceWith(
+            "computeScaleFactor(srcSize, dstSize)",
+            "androidx.compose.ui.layout"
+        )
+    )
+    fun scale(srcSize: Size, dstSize: Size): Float =
+        // Returning scaleX here as previous implementations of ContentScale all provided
+        // uniform scale values which were identical for both scaleX and scaleY
+        computeScaleFactor(srcSize, dstSize).scaleX
 
     /**
      * Companion object containing commonly used [ContentScale] implementations
@@ -51,8 +65,10 @@
          */
         @Stable
         val Crop = object : ContentScale {
-            override fun scale(srcSize: Size, dstSize: Size): Float =
-                computeFillMaxDimension(srcSize, dstSize)
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+                computeFillMaxDimension(srcSize, dstSize).let {
+                    ScaleFactor(it, it)
+                }
         }
 
         /**
@@ -65,8 +81,10 @@
          */
         @Stable
         val Fit = object : ContentScale {
-            override fun scale(srcSize: Size, dstSize: Size): Float =
-                computeFillMinDimension(srcSize, dstSize)
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+                computeFillMinDimension(srcSize, dstSize).let {
+                    ScaleFactor(it, it)
+                }
         }
 
         /**
@@ -76,8 +94,10 @@
          */
         @Stable
         val FillHeight = object : ContentScale {
-            override fun scale(srcSize: Size, dstSize: Size): Float =
-                computeFillHeight(srcSize, dstSize)
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+                computeFillHeight(srcSize, dstSize).let {
+                    ScaleFactor(it, it)
+                }
         }
 
         /**
@@ -87,8 +107,10 @@
          */
         @Stable
         val FillWidth = object : ContentScale {
-            override fun scale(srcSize: Size, dstSize: Size): Float =
-                computeFillWidth(srcSize, dstSize)
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+                computeFillWidth(srcSize, dstSize).let {
+                    ScaleFactor(it, it)
+                }
         }
 
         /**
@@ -102,19 +124,37 @@
          */
         @Stable
         val Inside = object : ContentScale {
-            override fun scale(srcSize: Size, dstSize: Size): Float =
-                if (srcSize.width <= dstSize.width && srcSize.height <= dstSize.height) {
-                    OriginalScale
+
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor {
+                return if (srcSize.width <= dstSize.width &&
+                    srcSize.height <= dstSize.height
+                ) {
+                    ScaleFactor(1.0f, 1.0f)
                 } else {
-                    computeFillMinDimension(srcSize, dstSize)
+                    computeFillMinDimension(srcSize, dstSize).let {
+                        ScaleFactor(it, it)
+                    }
                 }
+            }
         }
 
         /**
          * Do not apply any scaling to the source
          */
         @Stable
-        val None = FixedScale(OriginalScale)
+        val None = FixedScale(1.0f)
+
+        /**
+         * Scale horizontal and vertically non-uniformly to fill the destination bounds.
+         */
+        @Stable
+        val FillBounds = object : ContentScale {
+            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+                ScaleFactor(
+                    computeFillWidth(srcSize, dstSize),
+                    computeFillHeight(srcSize, dstSize)
+                )
+        }
     }
 }
 
@@ -124,7 +164,8 @@
  */
 @Immutable
 data class FixedScale(val value: Float) : ContentScale {
-    override fun scale(srcSize: Size, dstSize: Size): Float = value
+    override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+        ScaleFactor(value, value)
 }
 
 private fun computeFillMaxDimension(srcSize: Size, dstSize: Size): Float {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
new file mode 100644
index 0000000..256ac27
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.layout
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.util.packFloats
+import androidx.compose.ui.util.toStringAsFixed
+import androidx.compose.ui.util.unpackFloat1
+import androidx.compose.ui.util.unpackFloat2
+
+/**
+ * Constructs a [ScaleFactor] from the given x and y scale values
+ */
+@Stable
+fun ScaleFactor(scaleX: Float, scaleY: Float) = ScaleFactor(packFloats(scaleX, scaleY))
+
+/**
+ * Holds 2 dimensional scaling factors for horizontal and vertical axes
+ */
+@Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
+inline class ScaleFactor(@PublishedApi internal val packedValue: Long) {
+
+    /**
+     * Returns the scale factor to apply along the horizontal axis
+     */
+    @Stable
+    val scaleX: Float
+        get() {
+            // Explicitly compare against packed values to avoid
+            // auto-boxing of ScaleFactor.Unspecified
+            check(this.packedValue != ScaleFactor.Unspecified.packedValue) {
+                "ScaleFactor is unspecified"
+            }
+            return unpackFloat1(packedValue)
+        }
+
+    /**
+     * Returns the scale factor to apply along the vertical axis
+     */
+    @Stable
+    val scaleY: Float
+        get() {
+            // Explicitly compare against packed values to avoid
+            // auto-boxing of Size.Unspecified
+            check(this.packedValue != ScaleFactor.Unspecified.packedValue) {
+                "ScaleFactor is unspecified"
+            }
+            return unpackFloat2(packedValue)
+        }
+
+    @Suppress("NOTHING_TO_INLINE")
+    @Stable
+    inline operator fun component1(): Float = scaleX
+
+    @Suppress("NOTHING_TO_INLINE")
+    @Stable
+    inline operator fun component2(): Float = scaleY
+
+    /**
+     * Returns a copy of this ScaleFactor instance optionally overriding the
+     * scaleX or scaleY parameters
+     */
+    fun copy(scaleX: Float = this.scaleX, scaleY: Float = this.scaleY) = ScaleFactor(scaleX, scaleY)
+
+    /**
+     * Multiplication operator.
+     *
+     * Returns a [ScaleFactor] with scale x and y values multiplied by the operand
+     */
+    @Stable
+    operator fun times(operand: Float) = ScaleFactor(scaleX * operand, scaleY * operand)
+
+    /**
+     * Division operator.
+     *
+     * Returns a [ScaleFactor] with scale x and y values divided by the operand
+     */
+    @Stable
+    operator fun div(operand: Float) = ScaleFactor(scaleX / operand, scaleY / operand)
+
+    override fun toString() = "ScaleFactor(${scaleX.toStringAsFixed(1)}, " +
+        "${scaleY.toStringAsFixed(1)})"
+
+    companion object {
+
+        /**
+         * A ScaleFactor whose [scaleX] and [scaleY] parameters are unspecified. This is a sentinel
+         * value used to initialize a non-null parameter.
+         * Access to scaleX or scaleY on an unspecified size is not allowed
+         */
+        @Stable
+        val Unspecified = ScaleFactor(Float.NaN, Float.NaN)
+    }
+}
+
+/**
+ * Multiplication operator with [Size].
+ *
+ * Return a new [Size] with the width and height multiplied by the [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun Size.times(scaleFactor: ScaleFactor): Size =
+    Size(this.width * scaleFactor.scaleX, this.height * scaleFactor.scaleY)
+
+/**
+ * Multiplication operator with [Size] with reverse parameter types to maintain
+ * commutative properties of multiplication
+ *
+ * Return a new [Size] with the width and height multiplied by the [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun ScaleFactor.times(size: Size): Size = size * this
+
+/**
+ * Division operator with [Size]
+ *
+ * Return a new [Size] with the width and height divided by [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun Size.div(scaleFactor: ScaleFactor): Size =
+    Size(width / scaleFactor.scaleX, height / scaleFactor.scaleY)
+
+/**
+ * Linearly interpolate between two [ScaleFactor] parameters
+ *
+ * The [fraction] argument represents position on the timeline, with 0.0 meaning
+ * that the interpolation has not started, returning [start] (or something
+ * equivalent to [start]), 1.0 meaning that the interpolation has finished,
+ * returning [stop] (or something equivalent to [stop]), and values in between
+ * meaning that the interpolation is at the relevant point on the timeline
+ * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
+ * 1.0, so negative values and values greater than 1.0 are valid (and can
+ * easily be generated by curves).
+ *
+ * Values for [fraction] are usually obtained from an [Animation<Float>], such as
+ * an `AnimationController`.
+ */
+@Stable
+fun lerp(start: ScaleFactor, stop: ScaleFactor, fraction: Float): ScaleFactor {
+    return ScaleFactor(
+        androidx.compose.ui.util.lerp(start.scaleX, stop.scaleX, fraction),
+        androidx.compose.ui.util.lerp(start.scaleY, stop.scaleY, fraction)
+    )
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilterTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilterTest.kt
index 86d9555..20cffbc 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilterTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DoubleTapGestureFilterTest.kt
@@ -18,6 +18,7 @@
 
 package androidx.compose.ui.gesture
 
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.customevents.DelayUpEvent
 import androidx.compose.ui.gesture.customevents.DelayUpMessage
@@ -29,6 +30,8 @@
 import androidx.compose.ui.input.pointer.invokeOverAllPasses
 import androidx.compose.ui.input.pointer.moveTo
 import androidx.compose.ui.input.pointer.up
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.milliseconds
 import com.google.common.truth.Truth.assertThat
@@ -40,6 +43,7 @@
 import com.nhaarman.mockitokotlin2.verify
 import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
 import kotlinx.coroutines.CoroutineScope
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -68,6 +72,12 @@
         filter.>
         filter.doubleTapTimeout = DoubleTapTimeoutMillis
         filter.onInit(customEventDispatcher)
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun tearDown() {
+        isDebugInspectorInfoEnabled = false
     }
 
     // Tests that verify conditions under which onDoubleTap will not be called.
@@ -971,4 +981,13 @@
             .retainHitPaths(setOf(PointerId(456)))
         verifyNoMoreInteractions(customEventDispatcher)
     }
+
+    @Test
+    fun testInspectableValue() {
+        val onDoubleTap: (Offset) -> Unit = {}
+        val modifier = Modifier.doubleTapGestureFilter(onDoubleTap) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("doubleTapGestureFilter")
+        assertThat(modifier.valueOverride).isEqualTo(onDoubleTap)
+        assertThat(modifier.inspectableElements.asIterable()).isEmpty()
+    }
 }
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
index 8d646fc..7134996 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
@@ -27,92 +27,112 @@
 
     @Test
     fun testScaleNone() {
-        val scale = ContentScale.None.scale(
+        val scale = ContentScale.None.computeScaleFactor(
             srcSize = Size(100.0f, 100.0f),
             dstSize = Size(200.0f, 200.0f)
         )
-        assertEquals(1.0f, scale)
+        assertEquals(1.0f, scale.scaleX)
+        assertEquals(1.0f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleFit() {
-        val scale = ContentScale.Fit.scale(
+        val scale = ContentScale.Fit.computeScaleFactor(
             srcSize = Size(200.0f, 100.0f),
             dstSize = Size(100.0f, 200.0f)
         )
-        assertEquals(.5f, scale)
+        assertEquals(.5f, scale.scaleX)
+        assertEquals(.5f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleFillWidth() {
-        val scale = ContentScale.FillWidth.scale(
+        val scale = ContentScale.FillWidth.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(100.0f, 200.0f)
         )
-        assertEquals(0.25f, scale)
+        assertEquals(0.25f, scale.scaleX)
+        assertEquals(0.25f, scale.scaleY)
     }
 
     @Test
     fun testScaleFillHeight() {
-        val scale = ContentScale.FillHeight.scale(
+        val scale = ContentScale.FillHeight.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(100.0f, 200.0f)
         )
-        assertEquals(2.0f, scale)
+        assertEquals(2.0f, scale.scaleX)
+        assertEquals(2.0f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleCrop() {
-        val scale = ContentScale.Crop.scale(
+        val scale = ContentScale.Crop.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(100.0f, 200.0f)
         )
-        assertEquals(2.0f, scale)
+        assertEquals(2.0f, scale.scaleX)
+        assertEquals(2.0f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleInside() {
-        val scale = ContentScale.Inside.scale(
+        val scale = ContentScale.Inside.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(100.0f, 200.0f)
         )
-        assertEquals(0.25f, scale)
+        assertEquals(0.25f, scale.scaleX)
+        assertEquals(0.25f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleInsideLargeDst() {
         // If the src is smaller than the destination, ensure no scaling is done
-        val scale = ContentScale.Inside.scale(
+        val scale = ContentScale.Inside.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(900.0f, 200.0f)
         )
-        assertEquals(1.0f, scale)
+        assertEquals(1.0f, scale.scaleX)
+        assertEquals(1.0f, scale.scaleY)
     }
 
     @Test
     fun testContentFitInsideLargeDst() {
-        val scale = ContentScale.Fit.scale(
+        val scale = ContentScale.Fit.computeScaleFactor(
             srcSize = Size(400.0f, 100.0f),
             dstSize = Size(900.0f, 200.0f)
         )
-        assertEquals(2.0f, scale)
+        assertEquals(2.0f, scale.scaleX)
+        assertEquals(2.0f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleCropWidth() {
-        val scale = ContentScale.Crop.scale(
+        val scale = ContentScale.Crop.computeScaleFactor(
             srcSize = Size(100.0f, 400.0f),
             dstSize = Size(200.0f, 200.0f)
         )
-        assertEquals(2.00f, scale)
+        assertEquals(2.00f, scale.scaleX)
+        assertEquals(2.00f, scale.scaleY)
     }
 
     @Test
     fun testContentScaleCropHeight() {
-        val scale = ContentScale.Crop.scale(
+        val scale = ContentScale.Crop.computeScaleFactor(
             srcSize = Size(300.0f, 100.0f),
             dstSize = Size(200.0f, 200.0f)
         )
-        assertEquals(2.00f, scale)
+        assertEquals(2.00f, scale.scaleX)
+        assertEquals(2.00f, scale.scaleY)
+    }
+
+    @Test
+    fun testContentScaleFillBoundsUp() {
+        val scale = ContentScale.FillBounds.computeScaleFactor(
+            srcSize = Size(100f, 100f),
+            dstSize = Size(300f, 700f)
+        )
+        assertEquals(3.0f, scale.scaleX)
+        assertEquals(7.0f, scale.scaleY)
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
new file mode 100644
index 0000000..f2b8c87
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.layout
+
+import androidx.compose.ui.geometry.Size
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ScaleFactorTest {
+
+    @Test
+    fun testScaleFactorConstructor() {
+        val scaleFactor = ScaleFactor(2f, 3f)
+        assertEquals(2f, scaleFactor.scaleX)
+        assertEquals(3f, scaleFactor.scaleY)
+    }
+
+    @Test
+    fun testDestructuring() {
+        val (scaleX, scaleY) = ScaleFactor(7f, 12f)
+        assertEquals(7f, scaleX)
+        assertEquals(12f, scaleY)
+    }
+
+    @Test
+    fun testCopy() {
+        val scaleFactor = ScaleFactor(11f, 4f)
+        assertEquals(scaleFactor, scaleFactor.copy())
+    }
+
+    @Test
+    fun testCopyOverwriteScaleX() {
+        val scaleFactor = ScaleFactor(7f, 2f)
+        assertEquals(ScaleFactor(3f, 2f), scaleFactor.copy(scaleX = 3f))
+    }
+
+    @Test
+    fun testCopyOverwriteScaleY() {
+        val scaleFactor = ScaleFactor(2f, 9f)
+        assertEquals(ScaleFactor(2f, 27f), scaleFactor.copy(scaleY = 27f))
+    }
+
+    @Test
+    fun testScaleFactorMultiplication() {
+        assertEquals(ScaleFactor(2f, 8f), ScaleFactor(1f, 4f) * 2f)
+    }
+
+    @Test
+    fun testScaleFactorDivision() {
+        assertEquals(ScaleFactor(1f, 4f), ScaleFactor(2f, 8f) / 2f)
+    }
+
+    @Test
+    fun testUnspecifiedScaleXQueryThrows() {
+        try {
+            ScaleFactor.Unspecified.scaleX
+            fail("Attempt to access ScaleFactor.Unspecified.scaleX is not allowed")
+        } catch (e: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testUnspecifiedScaleYQueryThrows() {
+        try {
+            ScaleFactor.Unspecified.scaleY
+            fail("Attempt to access ScaleFactor.Unspecified.scaleY is not allowed")
+        } catch (e: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testSizeMultiplication() {
+        val scaleFactor = ScaleFactor(2f, 3f)
+        val size = Size(100f, 200f)
+        val expected = Size(200f, 600f)
+        // verify commutative property of multiplication
+        assertEquals(expected, size * scaleFactor)
+        assertEquals(expected, scaleFactor * size)
+    }
+
+    @Test
+    fun testScaleFactorLerp() {
+        val scaleFactor1 = ScaleFactor(1f, 10f)
+        val scaleFactor2 = ScaleFactor(3f, 20f)
+        assertEquals(ScaleFactor(2f, 15f), lerp(scaleFactor1, scaleFactor2, 0.5f))
+    }
+
+    @Test
+    fun testSizeDivision() {
+        assertEquals(Size(1f, 2f), Size(100f, 300f) / ScaleFactor(100f, 150f))
+    }
+}
\ No newline at end of file
diff --git a/core/core-appdigest/build.gradle b/core/core-appdigest/build.gradle
index b96a585..1625b7e 100644
--- a/core/core-appdigest/build.gradle
+++ b/core/core-appdigest/build.gradle
@@ -33,7 +33,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0")
+    implementation project(path: ':core:core')
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/development/build_log_processor.sh b/development/build_log_processor.sh
index 3f757b3..46257dd 100755
--- a/development/build_log_processor.sh
+++ b/development/build_log_processor.sh
@@ -47,13 +47,26 @@
 SCRIPT_PATH="$(cd $(dirname $0) && pwd)"
 CHECKOUT="$(cd "$SCRIPT_PATH/../../.." && pwd)"
 if [ -n "$DIST_DIR" ]; then
-  LOG_DIR="$DIST_DIR"
+  LOG_DIR="$DIST_DIR/logs"
 else
-  LOG_DIR="$CHECKOUT/out/dist"
+  LOG_DIR="$CHECKOUT/out/dist/logs"
 fi
 
 mkdir -p "$LOG_DIR"
 logFile="$LOG_DIR/gradle.log"
+
+# Move any preexisting $logFile to make room for a new one
+# After moving $logFile several times it eventually gets deleted
+function rotateLogs() {
+  iPlus1="10"
+  for i in $(seq 9 -1 1); do
+    mv "$LOG_DIR/gradle.${i}.log" "$LOG_DIR/gradle.${iPlus1}.log" 2>/dev/null || true
+    iPlus1=$i
+  done
+  mv $logFile "$LOG_DIR/gradle.1.log" 2>/dev/null || true
+}
+rotateLogs
+
 rm -f "$logFile"
 # Save OUT_DIR and some other variables into the log file so build_log_simplifier.py can
 # identify them later
@@ -63,6 +76,10 @@
 fi
 echo "DIST_DIR=$DIST_DIR" | tee -a $logFile
 echo "CHECKOUT=$CHECKOUT" | tee -a $logFile
+if [ "$GRADLE_USER_HOME" == "" ]; then
+  GRADLE_USER_HOME="$(cd && pwd)/.gradle"
+fi
+echo "GRADLE_USER_HOME=$GRADLE_USER_HOME" | tee -a $logFile
 programName="$1"
 shift
 if "$programName" "$@" > >(tee -a "$logFile") 2>&1; then
diff --git a/development/build_log_simplifier/build_log_simplifier.py b/development/build_log_simplifier/build_log_simplifier.py
index 3398ea0..cef0a79 100755
--- a/development/build_log_simplifier/build_log_simplifier.py
+++ b/development/build_log_simplifier/build_log_simplifier.py
@@ -139,7 +139,7 @@
             prev_line_is_boring = True
         elif line.startswith("\tat java.base"):
             if not prev_line_is_boring:
-                result.append("\tat java.base...")
+                result.append("\tat java.base...\n")
             prev_line_is_boring = True
         else:
             result.append(line)
@@ -185,9 +185,16 @@
           result.append(line)
   return result
 
-def get_exemptions_path():
+
+# Returns the path of the config file holding exemptions for deterministic/consistent output.
+# These exemptions can be garbage collected via the `--gc` argument
+def get_deterministic_exemptions_path():
     return os.path.join(dir_of_this_script, "build_log_simplifier/messages.ignore")
 
+# Returns the path of the config file holding exemptions for nondetermistic/flaky output.
+# These exemptions will not be garbage collected via the `--gc` argument
+def get_flake_exemptions_path():
+    return os.path.join(dir_of_this_script, "build_log_simplifier/message-flakes.ignore")
 
 # Returns a regexes_matcher that matches what is described by our config file
 # Ignores comments and ordering in our config file
@@ -220,7 +227,7 @@
         regexes.append(line)
     return regexes_matcher(regexes)
 
-def remove_configured_uninteresting_lines(lines, config_lines, validate_no_duplicates):
+def remove_by_regexes(lines, config_lines, validate_no_duplicates):
     fast_matcher = build_exemptions_matcher(config_lines)
     result = []
     for line in lines:
@@ -313,11 +320,13 @@
     out_dir = None
     dist_dir = None
     checkout_dir = None
+    gradle_user_home = None
     # we read checkout_root from the log file in case this build was run in a location,
     # such as on a build server
     out_marker = "OUT_DIR="
     dist_marker = "DIST_DIR="
     checkout_marker = "CHECKOUT="
+    gradle_user_home_marker="GRADLE_USER_HOME="
     for line in lines:
         if line.startswith(out_marker):
             out_dir = line.split(out_marker)[1].strip()
@@ -328,11 +337,18 @@
         if line.startswith(checkout_marker):
             checkout_dir = line.split(checkout_marker)[1].strip()
             continue
-        if out_dir is not None and dist_dir is not None and checkout_dir is not None:
+        if line.startswith(gradle_user_home_marker):
+            gradle_user_home = line.split(gradle_user_home_marker)[1].strip()
+            continue
+        if out_dir is not None and dist_dir is not None and checkout_dir is not None and gradle_user_home is not None:
             break
 
     # Remove any mentions of these paths, and replace them with consistent values
+    # Make sure to put these paths in the correct order so that more-specific paths will
+    # be matched first
     remove_paths = collections.OrderedDict()
+    if gradle_user_home is not None:
+        remove_paths[gradle_user_home] = "$GRADLE_USER_HOME"
     if dist_dir is not None:
         remove_paths[dist_dir] = "$DIST_DIR"
     if out_dir is not None:
@@ -516,7 +532,10 @@
         lines = normalize_paths(lines)
         all_lines += lines
     # load configuration
-    exemption_regexes_from_file = readlines(get_exemptions_path())
+    flake_exemption_regexes = readlines(get_flake_exemptions_path())
+    deterministic_exemption_regexes = readlines(get_deterministic_exemptions_path())
+    exemption_regexes = flake_exemption_regexes + deterministic_exemption_regexes
+    # load configuration
     # remove lines we're not interested in
     update = arguments.update or arguments.gc
     validate = update or arguments.validate
@@ -525,15 +544,18 @@
         interesting_lines = select_failing_task_output(interesting_lines)
     interesting_lines = shorten_uninteresting_stack_frames(interesting_lines)
     interesting_lines = remove_known_uninteresting_lines(interesting_lines)
-    interesting_lines = remove_configured_uninteresting_lines(interesting_lines, exemption_regexes_from_file, validate)
+    interesting_lines = remove_by_regexes(interesting_lines, exemption_regexes, validate)
     interesting_lines = collapse_tasks_having_no_output(interesting_lines)
     interesting_lines = collapse_consecutive_blank_lines(interesting_lines)
 
     # process results
     if update:
         if arguments.gc or len(interesting_lines) != 0:
-            update_path = get_exemptions_path()
-            suggested = generate_suggested_exemptions(all_lines, exemption_regexes_from_file, arguments.gc)
+            update_path = get_deterministic_exemptions_path()
+            # filter out any inconsistently observed messages so we don't try to exempt them twice
+            all_lines = remove_by_regexes(all_lines, flake_exemption_regexes, validate)
+            # update the deterministic exemptions file based on the result
+            suggested = generate_suggested_exemptions(all_lines, deterministic_exemption_regexes, arguments.gc)
             writelines(update_path, suggested)
             print("build_log_simplifier.py updated exemptions " + update_path)
     elif validate:
@@ -544,13 +566,16 @@
             print("".join(interesting_lines))
             print("Error: build_log_simplifier.py found " + str(len(interesting_lines)) + " new messages found in " + ",".join(log_paths) + ".")
             new_exemptions_path = log_paths[0] + ".ignore"
-            suggested = generate_suggested_exemptions(all_lines, exemption_regexes_from_file, arguments.gc)
+            # filter out any inconsistently observed messages so we don't try to exempt them twice
+            all_lines = remove_by_regexes(all_lines, flake_exemption_regexes, validate)
+            # update deterministic exemptions file based on the result
+            suggested = generate_suggested_exemptions(all_lines, deterministic_exemption_regexes, arguments.gc)
             writelines(new_exemptions_path, suggested)
             print("")
             print("Please fix or suppress these new messages in the tool that generates them.")
             print("If you cannot, then you can exempt them by doing:")
             print("")
-            print("  1. cp " + new_exemptions_path + " " + get_exemptions_path() + " # or, if this script is running on the build server, you may download this file from there")
+            print("  1. cp " + new_exemptions_path + " " + get_deterministic_exemptions_path() + " # or, if this script is running on the build server, you may download this file from there")
             print("  2. modify the new lines to be more generalized (they are regular expressions) if it is more important to preemptively exempt similar messages than to be notified of new, similar messages")
             print("")
             print("Note that if you exempt these messages by updating the exemption file, this will suppress these messages in the output of CI builds but not in Android Studio.")
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
new file mode 100644
index 0000000..3270301
--- /dev/null
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -0,0 +1,13 @@
+# This file is a list of regexes of innocuous messages that might appear in stdout/stderr
+# Messages that are expected to appear in a build should be exempted in messages.ignore instead, because that file is subject to garbage collection whereas this one is not
+# Before adding a suppression to this file, please attempt to suppress the output inside the generating tool instead.
+# > Task :sqlite:integration-tests:inspection-room-testapp:kaptDebugAndroidTestKotlin
+ZipFile closed
+# > Task :compose:internal-lint-checks:lint
+Classpath entry points to a non\-existent location\: \$OUT_DIR\/ui\/compose\/internal\-lint\-checks\/build\/classes\/kotlin\/main
+# > Task :compose:animation:animation-core:testDebugUnitTest
+androidx\.compose\.animation\.core\.AnimationClockTest \> testSubscriptionDuringFrameCallback FAILED
+kotlin\.UninitializedPropertyAccessException at AnimationClockTest\.kt\:[0-9]+
+[0-9]+ tests completed\, [0-9]+ failed
+There were failing tests\. See the report at\: file\:\/\/\$OUT_DIR\/ui\/compose\/animation\/animation\-core\/build\/reports\/tests\/testDebugUnitTest\/index\.html
+
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 3ec5947..5513fe1 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -1,6 +1,8 @@
-# This file is a list of regexes of messages that are permitted to appear in stdout/stderr
-# Comments and blank lines are ignored
-# Before adding a suppression to this file, please attempt to suppress the output inside the generating tool instead
+# This file is a list of regexes of messages that are expected to appear in stdout/stderr.
+# Comments and blank lines are ignored.
+# Before adding a suppression to this file, please attempt to suppress the output inside the generating tool instead.
+# Messages that only sometimes appear in stdout/stderr should be exempted in message-flakes.ignore, because this file (messages.ignore) may be automatically garbage collected whereas that one will not be.
+
 # > Task :docs-runner:dokkaJavaPublicDocs
 (logging: loading modules: \[java\.se.*)|(.*No file found when processing Java @sample.*)
 # > Task :contentaccess:integration-tests:testapp:kaptDebugAndroidTestKotlin
@@ -10,6 +12,7 @@
 OUT_DIR=\$OUT_DIR
 DIST_DIR=\$DIST_DIR
 CHECKOUT=\$CHECKOUT
+GRADLE_USER_HOME=\$GRADLE_USER_HOME
 Downloading file:\$SUPPORT/gradle/wrapper/\.\./\.\./\.\./\.\./tools/external/gradle/gradle\-[0-9]+\.[0-9]+\-bin\.zip
 \.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%\.\.\.\.\.\.\.\.\.\.[0-9]+%
 Welcome to Gradle [0-9]+\.[0-9]+!
@@ -56,9 +59,9 @@
 BUILD SUCCESSFUL in .*
 [0-9]+ actionable task: [0-9]+ executed
 # > Task :docs-fake:processPublicReleaseMainManifest
-\[androidx\.wear\:wear\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\] \$OUT_DIR\/\.gradle\/caches\/transforms\-[0-9]+\/files\-[0-9]+\.[0-9]+\/[0-9a-f]{32}\/wear\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\/AndroidManifest\.xml Warning\:
+\[androidx\.wear\:wear\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\] \$GRADLE_USER_HOME\/caches\/transforms\-[0-9]+\/files\-[0-9]+\.[0-9]+\/[0-9a-f]{32}\/wear\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\/AndroidManifest\.xml Warning\:
 Package name \'androidx\.wear\' used in\: androidx\.wear\:wear\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\, androidx\.wear\:wear\-input\-testing\:[0-9]+\.[0-9]+\.[0-9]+\-beta[0-9]+\.
-\[androidx\.compose\.material\:material\-icons\-extended\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\] \$OUT_DIR\/\.gradle\/caches\/transforms\-[0-9]+\/files\-[0-9]+\.[0-9]+\/[0-9a-f]{32}\/material\-icons\-extended\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\/AndroidManifest\.xml Warning\:
+\[androidx\.compose\.material\:material\-icons\-extended\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\] \$GRADLE_USER_HOME\/caches\/transforms\-[0-9]+\/files\-[0-9]+\.[0-9]+\/[0-9a-f]{32}\/material\-icons\-extended\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\/AndroidManifest\.xml Warning\:
 Package name \'androidx\.compose\.material\.icons\' used in\: androidx\.compose\.material\:material\-icons\-extended\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\, androidx\.compose\.material\:material\-icons\-core\:[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\.
 \$SUPPORT/docs\-fake/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
 application@android:appComponentFactory was tagged at AndroidManifest\.xml:[0-9]+ to replace other declarations but no other declaration present
@@ -427,7 +430,7 @@
 # > Task :camera:integration-tests:camera-testapp-core:compileDebugJavaWithJavac
 Note: \$SUPPORT/camera/integration\-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity\.java uses or overrides a deprecated API\.
 # > Task :room:integration-tests:room-testapp:processDebugMainManifest
-\[androidx\.vectordrawable:vectordrawable\-animated:[0-9]+\.[0-9]+\.[0-9]+\] \$OUT_DIR/\.gradle/caches/transforms\-[0-9]+/files\-[0-9]+\.[0-9]+/[0-9a-f]{32}/vectordrawable\-animated\-[0-9]+\.[0-9]+\.[0-9]+/AndroidManifest\.xml Warning:
+\[androidx\.vectordrawable:vectordrawable\-animated:[0-9]+\.[0-9]+\.[0-9]+\] \$GRADLE_USER_HOME/caches/transforms\-[0-9]+/files\-[0-9]+\.[0-9]+/[0-9a-f]{32}/vectordrawable\-animated\-[0-9]+\.[0-9]+\.[0-9]+/AndroidManifest\.xml Warning:
 Package name 'androidx\.vectordrawable' used in: androidx\.vectordrawable:vectordrawable\-animated:[0-9]+\.[0-9]+\.[0-9]+, androidx\.vectordrawable:vectordrawable:[0-9]+\.[0-9]+\.[0-9]+\.
 # > Task :lifecycle:integration-tests:lifecycle-testapp:compileDebugJavaWithJavac
 Note: \$SUPPORT/lifecycle/integration\-tests/testapp/src/main/java/androidx/lifecycle/testapp/FrameworkLifecycleRegistryActivity\.java uses or overrides a deprecated API\.
@@ -599,6 +602,7 @@
 # > Task :appsearch:appsearch-compiler:lint
 \$SUPPORT/appsearch/compiler/lint\-baseline\.xml: Information: [0-9]+ error was filtered out because it is listed in the baseline file, lint\-baseline\.xml
 \[LintBaseline\]
+[0-9]+ errors, [0-9]+ warnings \([0-9]+ errors filtered by baseline lint\-baseline\.xml\)
 [0-9]+ errors, [0-9]+ warnings \([0-9]+ error filtered by baseline lint\-baseline\.xml\)
 # > Task :slice-view:generateApi
 src/main/java/androidx/slice/widget/ListContent\.java:[0-9]+: warning: Parameter of hidden type SliceContent in androidx\.slice\.widget\.ListContent\.getListHeight\(\) \[UnavailableSymbol\]
@@ -683,10 +687,7 @@
 Package name.*test' used in: .*AndroidManifest.*xml.*
 # > Task :room:integration-tests:room-testapp-autovalue:compileDebugAndroidTestJavaWithJavac
 Stream closed
-# > Task :sqlite:integration-tests:inspection-room-testapp:kaptDebugAndroidTestKotlin
-ZipFile closed
 # > Task :compose:internal-lint-checks:lint
-Classpath entry points to a non\-existent location\: \$OUT_DIR\/ui\/compose\/internal\-lint\-checks\/build\/classes\/kotlin\/main
 # > Task :leanback:leanback-paging:generateApi
 w\: Runtime JAR files in the classpath have the version [0-9]+\.[0-9]+\, which is older than the API version [0-9]+\.[0-9]+\. Consider using the runtime of version [0-9]+\.[0-9]+\, or pass \'\-api\-version [0-9]+\.[0-9]+\' explicitly to restrict the available APIs to the runtime of version [0-9]+\.[0-9]+\. You can also pass \'\-language\-version [0-9]+\.[0-9]+\' instead\, which will restrict not only the APIs to the specified version\, but also the language features
 w\: \$CHECKOUT\/prebuilts\/androidx\/external\/org\/jetbrains\/kotlin\/kotlin\-stdlib\/[0-9]+\.[0-9]+\.[0-9]+\/kotlin\-stdlib\-[0-9]+\.[0-9]+\.[0-9]+\.jar\: Runtime JAR file has version [0-9]+\.[0-9]+ which is older than required for API version [0-9]+\.[0-9]+
@@ -727,11 +728,11 @@
 # > Task :wear:wear-input:compileDebugUnitTestJavaWithJavac
 Note: \$SUPPORT/wear/wear\-input/src/test/java/androidx/wear/input/WearableButtonsTest\.java uses or overrides a deprecated API\.
 # > Task :lifecycle:integration-tests:incrementality:compileTestKotlin
-\$OUT_DIR\/\.gradle\/wrapper\/dists\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\-bin\/[0-9a-z]+\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\/lib\/kotlin\-stdlib\-[0-9]+\.[0-9]+\.[0-9]+\.jar \(version [0-9]+\.[0-9]+\)
-\$OUT_DIR\/\.gradle\/wrapper\/dists\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\-bin\/[0-9a-z]+\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\/lib\/kotlin\-stdlib\-common\-[0-9]+\.[0-9]+\.[0-9]+\.jar \(version [0-9]+\.[0-9]+\)
-\$OUT_DIR\/\.gradle\/wrapper\/dists\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\-bin\/[0-9a-z]+\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\/lib\/kotlin\-stdlib\-jdk[0-9]+\-[0-9]+\.[0-9]+\.[0-9]+\.jar \(version [0-9]+\.[0-9]+\)
-\$OUT_DIR\/\.gradle\/wrapper\/dists\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\-bin\/[0-9a-z]+\/gradle\-[0-9]+\.[0-9]+\.[0-9]+\/lib\/kotlin\-reflect\-[0-9]+\.[0-9]+\.[0-9]+\.jar \(version [0-9]+\.[0-9]+\)
-w: Consider providing an explicit dependency on kotlin\-reflect [0-9]+\.[0-9]+ to prevent strange errors
+\$GRADLE_USER_HOME\/wrapper\/dists\/gradle\-[^/]*-bin\/[0-9a-z]+\/gradle\-[^/]*/lib\/kotlin\-stdlib\-[0-9.]*.jar \(version [^/]*\)
+\$GRADLE_USER_HOME\/wrapper\/dists\/gradle\-[^/]*-bin\/[0-9a-z]+\/gradle\-[^/]*/lib\/kotlin\-stdlib\-common\-[0-9.]*.jar \(version [^/]*\)
+\$GRADLE_USER_HOME\/wrapper\/dists\/gradle\-[^/]*-bin\/[0-9a-z]+\/gradle\-[^/]*/lib\/kotlin\-stdlib\-jdk[0-9]*-[0-9.]*.jar \(version [^/]*\)
+\$GRADLE_USER_HOME\/wrapper\/dists\/gradle\-[^/]*-bin\/[0-9a-z]+\/gradle\-[^/]*/lib\/kotlin\-reflect\-[0-9.]*.jar \(version [^/]*\)
+w: Consider providing an explicit dependency on kotlin\-reflect [^/]* to prevent strange errors
 # > Task :wear:wear-complications-rendering:compileDebugUnitTestJavaWithJavac
 Note: \$SUPPORT/wear/wear\-complications\-rendering/src/test/java/androidx/wear/complications/rendering/RoundedDrawableTest\.java uses or overrides a deprecated API\.
 # > Task :contentaccess:contentaccess-compiler:test
@@ -745,11 +746,6 @@
 # > 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 :compose:animation:animation-core:testDebugUnitTest
-androidx\.compose\.animation\.core\.AnimationClockTest \> testSubscriptionDuringFrameCallback FAILED
-kotlin\.UninitializedPropertyAccessException at AnimationClockTest\.kt\:[0-9]+
-[0-9]+ tests completed\, [0-9]+ failed
-There were failing tests\. See the report at\: file\:\/\/\$OUT_DIR\/ui\/compose\/animation\/animation\-core\/build\/reports\/tests\/testDebugUnitTest\/index\.html
 # > 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
@@ -784,4 +780,6 @@
 \-XXLanguage\:\+InlineClasses
 # > Task :docs-public:lintDebug
 Wrote HTML report to file://\$OUT_DIR/androidx/docs\-public/build/reports/lint\-results\-debug\.html
-Wrote XML report to file://\$OUT_DIR/androidx/docs\-public/build/reports/lint\-results\-debug\.xml
\ No newline at end of file
+Wrote XML report to file://\$OUT_DIR/androidx/docs\-public/build/reports/lint\-results\-debug\.xml
+# > Task :compose:compiler:compiler-hosted:lint
+\$SUPPORT/compose/compiler/compiler\-hosted/lint\-baseline\.xml: Information: [0-9]+ errors were filtered out because they are listed in the baseline file, lint\-baseline\.xml
diff --git a/leanback/leanback-paging/api/current.txt b/leanback/leanback-paging/api/current.txt
index 4376e7e..5c650ed 100644
--- a/leanback/leanback-paging/api/current.txt
+++ b/leanback/leanback-paging/api/current.txt
@@ -11,19 +11,19 @@
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher, kotlinx.coroutines.CoroutineDispatcher workerDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
-    method @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void addLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public T? get(int position);
-    method public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
+    method @Deprecated public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
     method public kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> getLoadStateFlow();
     method public void refresh();
-    method @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void removeLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public void retry();
     method public int size();
     method public suspend Object? submitData(androidx.paging.PagingData<T> pagingData, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void submitData(androidx.lifecycle.Lifecycle lifecycle, androidx.paging.PagingData<T> pagingData);
-    property public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
+    property @Deprecated public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
     property public final kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> loadStateFlow;
   }
 
diff --git a/leanback/leanback-paging/api/public_plus_experimental_current.txt b/leanback/leanback-paging/api/public_plus_experimental_current.txt
index 4376e7e..5c650ed 100644
--- a/leanback/leanback-paging/api/public_plus_experimental_current.txt
+++ b/leanback/leanback-paging/api/public_plus_experimental_current.txt
@@ -11,19 +11,19 @@
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher, kotlinx.coroutines.CoroutineDispatcher workerDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
-    method @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void addLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public T? get(int position);
-    method public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
+    method @Deprecated public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
     method public kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> getLoadStateFlow();
     method public void refresh();
-    method @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void removeLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public void retry();
     method public int size();
     method public suspend Object? submitData(androidx.paging.PagingData<T> pagingData, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void submitData(androidx.lifecycle.Lifecycle lifecycle, androidx.paging.PagingData<T> pagingData);
-    property public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
+    property @Deprecated public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
     property public final kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> loadStateFlow;
   }
 
diff --git a/leanback/leanback-paging/api/restricted_current.txt b/leanback/leanback-paging/api/restricted_current.txt
index 4376e7e..5c650ed 100644
--- a/leanback/leanback-paging/api/restricted_current.txt
+++ b/leanback/leanback-paging/api/restricted_current.txt
@@ -11,19 +11,19 @@
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher, kotlinx.coroutines.CoroutineDispatcher workerDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
     ctor public PagingDataAdapter(androidx.leanback.widget.PresenterSelector presenterSelector, androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
-    method @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void addDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void addLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public T? get(int position);
-    method public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
+    method @Deprecated public kotlinx.coroutines.flow.Flow<java.lang.Boolean> getDataRefreshFlow();
     method public kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> getLoadStateFlow();
     method public void refresh();
-    method @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
+    method @Deprecated @androidx.paging.ExperimentalPagingApi public void removeDataRefreshListener(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> listener);
     method public void removeLoadStateListener(kotlin.jvm.functions.Function1<? super androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public void retry();
     method public int size();
     method public suspend Object? submitData(androidx.paging.PagingData<T> pagingData, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void submitData(androidx.lifecycle.Lifecycle lifecycle, androidx.paging.PagingData<T> pagingData);
-    property public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
+    property @Deprecated public final kotlinx.coroutines.flow.Flow<java.lang.Boolean> dataRefreshFlow;
     property public final kotlinx.coroutines.flow.Flow<androidx.paging.CombinedLoadStates> loadStateFlow;
   }
 
diff --git a/leanback/leanback-paging/src/androidTest/java/androidx/leanback/paging/PagingDataAdapterTest.kt b/leanback/leanback-paging/src/androidTest/java/androidx/leanback/paging/PagingDataAdapterTest.kt
index 66d71f6..e696ccc 100644
--- a/leanback/leanback-paging/src/androidTest/java/androidx/leanback/paging/PagingDataAdapterTest.kt
+++ b/leanback/leanback-paging/src/androidTest/java/androidx/leanback/paging/PagingDataAdapterTest.kt
@@ -75,6 +75,7 @@
                 workerDispatcher = Dispatchers.Main
             )
         val refreshEvents = mutableListOf<Boolean>()
+        @Suppress("DEPRECATION")
         pagingDataAdapter.addDataRefreshListener { refreshEvents.add(it) }
         val pager = Pager(
             config = PagingConfig(
@@ -117,6 +118,7 @@
                 workerDispatcher = Dispatchers.Main
             )
         val refreshEvents = mutableListOf<Boolean>()
+        @Suppress("DEPRECATION")
         pagingDataAdapter.addDataRefreshListener { refreshEvents.add(it) }
         val pager = Pager(
             config = PagingConfig(
diff --git a/leanback/leanback-paging/src/main/java/androidx/leanback/paging/PagingDataAdapter.kt b/leanback/leanback-paging/src/main/java/androidx/leanback/paging/PagingDataAdapter.kt
index 7e7b671..d49710e 100644
--- a/leanback/leanback-paging/src/main/java/androidx/leanback/paging/PagingDataAdapter.kt
+++ b/leanback/leanback-paging/src/main/java/androidx/leanback/paging/PagingDataAdapter.kt
@@ -21,14 +21,14 @@
 import androidx.leanback.widget.PresenterSelector
 import androidx.lifecycle.Lifecycle
 import androidx.paging.AsyncPagingDataDiffer
-import androidx.paging.Pager
-import androidx.paging.PagingSource
-import androidx.paging.LoadState
-import androidx.paging.RemoteMediator
-import androidx.paging.LoadType
 import androidx.paging.CombinedLoadStates
 import androidx.paging.ExperimentalPagingApi
+import androidx.paging.LoadState
+import androidx.paging.LoadType
+import androidx.paging.Pager
 import androidx.paging.PagingData
+import androidx.paging.PagingSource
+import androidx.paging.RemoteMediator
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.ListUpdateCallback
 import kotlinx.coroutines.CoroutineDispatcher
@@ -52,12 +52,15 @@
             override fun onInserted(position: Int, count: Int) {
                 notifyItemRangeInserted(position, count)
             }
+
             override fun onRemoved(position: Int, count: Int) {
                 notifyItemRangeRemoved(position, count)
             }
+
             override fun onMoved(fromPosition: Int, toPosition: Int) {
                 notifyItemMoved(fromPosition, toPosition)
             }
+
             override fun onChanged(
                 position: Int,
                 count: Int,
@@ -250,7 +253,7 @@
     }
 
     /**
-     * Returns the number of items in the adapter.
+     * @return Total number of presented items, including placeholders.
      */
     override fun size(): Int {
         return differ.itemCount
@@ -269,6 +272,19 @@
      * displayed. The [Boolean] that is emitted is `true` if the new [PagingData] is empty,
      * `false` otherwise.
      */
+    @Deprecated(
+        message = "dataRefreshFlow is now redundant with the information passed from " +
+            "loadStateFlow and size(), and will be removed in a future alpha version",
+        replaceWith = ReplaceWith(
+            """loadStateFlow.map { it.source.refresh }
+                .filter { it is LoadState.NotLoading }
+                .distinctUntilChanged()""",
+            "androidx.paging.LoadState",
+            "kotlinx.coroutines.flow.distinctUntilChanged",
+            "kotlinx.coroutines.flow.filter",
+            "kotlinx.coroutines.flow.map",
+        )
+    )
     @ExperimentalPagingApi
     val dataRefreshFlow: Flow<Boolean>
         get() = differ.dataRefreshFlow
@@ -281,6 +297,10 @@
      *
      * @see removeDataRefreshListener
      */
+    @Deprecated(
+        "dataRefreshListener is now redundant with the information passed from loadStateListener " +
+            "and size(), and will be removed in a future alpha version"
+    )
     @ExperimentalPagingApi
     fun addDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
         differ.addDataRefreshListener(listener)
@@ -293,6 +313,10 @@
      *
      * @see addDataRefreshListener
      */
+    @Deprecated(
+        "dataRefreshListener is now redundant with the information passed from loadStateListener " +
+            "and size(), and will be removed in a future alpha version"
+    )
     @ExperimentalPagingApi
     fun removeDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
         differ.removeDataRefreshListener(listener)
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index b894031f6..e14a4da 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -32,6 +32,7 @@
             return listOf(
                 BanParcelableUsage.ISSUE,
                 BanConcurrentHashMap.ISSUE,
+                BanInappropriateExperimentalUsage.ISSUE,
                 BanKeepAnnotation.ISSUE,
                 BanTargetApiAnnotation.ISSUE,
                 SampledAnnotationEnforcer.MISSING_SAMPLED_ANNOTATION,
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
new file mode 100644
index 0000000..036fd19
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build.lint
+
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.getContainingUClass
+
+class BanInappropriateExperimentalUsage : Detector(), SourceCodeScanner {
+    override fun applicableAnnotations(): List<String>? = listOf(
+        JAVA_EXPERIMENTAL_ANNOTATION,
+        KOTLIN_OPT_IN_ANNOTATION,
+        KOTLIN_EXPERIMENTAL_ANNOTATION
+    )
+
+    override fun visitAnnotationUsage(
+        context: JavaContext,
+        usage: UElement,
+        type: AnnotationUsageType,
+        annotation: UAnnotation,
+        qualifiedName: String,
+        method: PsiMethod?,
+        referenced: PsiElement?,
+        annotations: List<UAnnotation>,
+        allMemberAnnotations: List<UAnnotation>,
+        allClassAnnotations: List<UAnnotation>,
+        allPackageAnnotations: List<UAnnotation>
+    ) {
+        when (qualifiedName) {
+            JAVA_EXPERIMENTAL_ANNOTATION,
+            JAVA_OPT_IN_ANNOTATION,
+            KOTLIN_EXPERIMENTAL_ANNOTATION,
+            KOTLIN_OPT_IN_ANNOTATION -> {
+                verifyExperimentalOrOptInUsageIsWithinSameGroup(
+                    context, usage, annotation
+                )
+            }
+        }
+    }
+
+    fun verifyExperimentalOrOptInUsageIsWithinSameGroup(
+        context: JavaContext,
+        usage: UElement,
+        annotation: UAnnotation
+    ) {
+        val declaringGroup = getApproximateAnnotationMavenGroup(annotation)
+        val usingGroup = getApproximateUsageSiteMavenGroup(usage)
+        // Don't flag if group is null for some reason (for now at least)
+        // Also exclude sample for now, since it doesn't work well with our workaround (includes
+        // class)
+        if (declaringGroup != null && usingGroup != null && declaringGroup != usingGroup &&
+            usingGroup != "sample"
+        ) {
+            context.report(
+                BanInappropriateExperimentalUsage.ISSUE, usage, context.getNameLocation(usage),
+                "Experimental/OptIn APIs should only be used from within the same library or " +
+                    "libraries within the same requireSameVersion group"
+            )
+        }
+    }
+
+    fun getApproximateAnnotationMavenGroup(annotation: UAnnotation): String? {
+        if (annotation.getContainingUClass() == null || annotation.getContainingUClass()!!
+            .qualifiedName == null
+        ) {
+            return null
+        }
+        return annotation.getContainingUClass()!!.qualifiedName!!.split(".").subList(0, 2)
+            .joinToString(".")
+    }
+
+    fun getApproximateUsageSiteMavenGroup(usage: UElement): String? {
+        if (usage.getContainingUClass() == null || usage.getContainingUClass()!!
+            .qualifiedName == null
+        ) {
+            return null
+        }
+        return usage.getContainingUClass()!!.qualifiedName!!.split(".").subList(0, 2)
+            .joinToString(".")
+    }
+
+    companion object {
+
+        private const val KOTLIN_EXPERIMENTAL_ANNOTATION = "kotlin.Experimental"
+
+        private const val JAVA_EXPERIMENTAL_ANNOTATION =
+            "androidx.annotation.experimental.Experimental"
+
+        private const val KOTLIN_OPT_IN_ANNOTATION =
+            "kotlin.OptIn"
+
+        private const val JAVA_OPT_IN_ANNOTATION =
+            "androidx.annotation.OptIn"
+
+        val ISSUE = Issue.create(
+            "IllegalExperimentalApiUsage",
+            "Experimental apis can only be used from within the same library or from libraries " +
+                "that are part of the same requireSameVersion maven group.",
+            "@RequiresOptIn and @Experimental APIs are considered alpha, and we do not allow " +
+                "non-atomic inter-library usage of alpha APIs.",
+            Category.CORRECTNESS, 5, Severity.ERROR,
+            Implementation(BanInappropriateExperimentalUsage::class.java, Scope.JAVA_FILE_SCOPE)
+        )
+    }
+}
\ No newline at end of file
diff --git a/navigation/benchmark/src/androidTest/AndroidManifest.xml b/navigation/benchmark/src/androidTest/AndroidManifest.xml
index 358a851..802e59d 100644
--- a/navigation/benchmark/src/androidTest/AndroidManifest.xml
+++ b/navigation/benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/navigation/navigation-compose/api/current.txt b/navigation/navigation-compose/api/current.txt
index 3210525..e9c9ce1 100644
--- a/navigation/navigation-compose/api/current.txt
+++ b/navigation/navigation-compose/api/current.txt
@@ -21,6 +21,7 @@
 
   public final class NavGraphBuilderKt {
     method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/api/public_plus_experimental_current.txt b/navigation/navigation-compose/api/public_plus_experimental_current.txt
index 3210525..e9c9ce1 100644
--- a/navigation/navigation-compose/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-compose/api/public_plus_experimental_current.txt
@@ -21,6 +21,7 @@
 
   public final class NavGraphBuilderKt {
     method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/api/restricted_current.txt b/navigation/navigation-compose/api/restricted_current.txt
index 3210525..e9c9ce1 100644
--- a/navigation/navigation-compose/api/restricted_current.txt
+++ b/navigation/navigation-compose/api/restricted_current.txt
@@ -21,6 +21,7 @@
 
   public final class NavGraphBuilderKt {
     method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
index 8c8b507..e1d2725 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
@@ -23,6 +23,7 @@
     "Navigation",
     listOf(
         ComposableDemo("Basic Nav Demo") { BasicNavDemo() },
+        ComposableDemo("Nested Nav Demo") { NestNavDemo() },
         ComposableDemo("Bottom Bar Nav Demo") { BottomBarNavDemo() },
         ComposableDemo("Navigation with Args") { NavWithArgsDemo() },
         ComposableDemo("Navigation by DeepLink") { NavByDeepLinkDemo() }
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NestedNavDemo.kt
similarity index 75%
copy from wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
copy to navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NestedNavDemo.kt
index 4b53020..c2790f1 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NestedNavDemo.kt
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.wear.watchface.data;
+package androidx.navigation.compose.demos
 
-/** @hide */
-parcelable ComplicationDetails;
\ No newline at end of file
+import androidx.compose.runtime.Composable
+import androidx.navigation.compose.samples.NestedNav
+
+@Composable
+fun NestNavDemo() {
+    NestedNav()
+}
\ No newline at end of file
diff --git a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
index 470c252..bb68ba3 100644
--- a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
+++ b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
@@ -40,6 +40,7 @@
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.navigate
+import androidx.navigation.compose.navigation
 import androidx.navigation.compose.rememberNavController
 
 sealed class Screen(val route: String, @StringRes val resourceId: Int) {
@@ -59,6 +60,19 @@
     }
 }
 
+@Sampled
+@Composable
+fun NestedNav() {
+    val navController = rememberNavController()
+    NavHost(navController, startDestination = "nested") {
+        navigation(startDestination = Screen.Profile.route, route = "nested") {
+            composable(Screen.Profile.route) { Profile(navController) }
+        }
+        composable(Screen.Dashboard.route) { Dashboard(navController) }
+        composable(Screen.Scrollable.route) { Scrollable(navController) }
+    }
+}
+
 @Composable
 fun Profile(navController: NavHostController) {
     Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
index d487ce2..8c82eae 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Rule
 import org.junit.Test
@@ -109,6 +110,27 @@
                 .isTrue()
         }
     }
+
+    @Test
+    fun testNavigationNested() {
+        lateinit var navController: TestNavHostController
+        composeTestRule.setContent {
+            navController = TestNavHostController(ContextAmbient.current)
+            navController.navigatorProvider.addNavigator(ComposeNavigator())
+
+            NavHost(navController, startDestination = firstRoute) {
+                navigation(startDestination = secondRoute, route = firstRoute) {
+                    composable(secondRoute) { }
+                }
+            }
+        }
+
+        composeTestRule.runOnUiThread {
+            assertWithMessage("Destination should be added to the graph")
+                .that(firstRoute in navController.graph)
+                .isTrue()
+        }
+    }
 }
 
 private const val firstRoute = "first"
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
index 3e3be1f..3cabaa2 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
@@ -19,8 +19,10 @@
 import androidx.compose.runtime.Composable
 import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavDeepLink
+import androidx.navigation.NavGraph
 import androidx.navigation.NavGraphBuilder
 import androidx.navigation.get
+import androidx.navigation.navigation
 
 /**
  * Add the [Composable] to the [NavGraphBuilder]
@@ -32,8 +34,8 @@
  */
 public fun NavGraphBuilder.composable(
     route: String,
-    arguments: List<NamedNavArgument> = listOf(),
-    deepLinks: List<NavDeepLink> = listOf(),
+    arguments: List<NamedNavArgument> = emptyList(),
+    deepLinks: List<NavDeepLink> = emptyList(),
     content: @Composable (NavBackStackEntry) -> Unit
 ) {
     addDestination(
@@ -52,3 +54,18 @@
         }
     )
 }
+
+/**
+ * Construct a nested [NavGraph]
+ *
+ * @sample androidx.navigation.compose.samples.NestedNav
+ */
+public fun NavGraphBuilder.navigation(
+    startDestination: String,
+    route: String,
+    builder: NavGraphBuilder.() -> Unit
+): Unit = navigation(
+    createRoute(route).hashCode(),
+    createRoute(startDestination).hashCode(),
+    builder
+)
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment.kt b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment.kt
index 2384503..d6b50c5 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment.kt
@@ -21,6 +21,7 @@
 import android.content.IntentSender
 import android.os.Bundle
 import android.util.Log
+import android.view.View
 import androidx.annotation.RestrictTo
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
@@ -72,24 +73,22 @@
         }
     }
 
-    override fun onResume() {
-        super.onResume()
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         if (navigated) {
             findNavController().popBackStack()
             return
         }
         var monitor = installViewModel.installMonitor
         if (monitor == null) {
-            Log.i(TAG, "onResume: monitor is null, navigating")
+            Log.i(TAG, "onViewCreated: monitor is null, navigating")
             navigate()
             monitor = installViewModel.installMonitor
         }
         if (monitor != null) {
-            Log.i(TAG, "onResume: monitor is now not null, observing")
-            monitor.status.observe(this, StateObserver(monitor))
+            Log.i(TAG, "onViewCreated: monitor is now not null, observing")
+            monitor.status.observe(viewLifecycleOwner, StateObserver(monitor))
         }
     }
-
     /**
      * Navigates to an installed dynamic feature module or kicks off installation.
      *
diff --git a/navigation/navigation-runtime-ktx/lint-baseline.xml b/navigation/navigation-runtime-ktx/lint-baseline.xml
new file mode 100644
index 0000000..010f48d
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-alpha06" client="gradle" variant="debug" version="4.2.0-alpha06">
+
+    <issue
+        id="IllegalExperimentalApiUsage"
+        message="Experimental/OptIn APIs should only be used from within the same library or libraries within the same requireSameVersion group"
+        errorLine1="public val NavController.currentBackStackEntryFlow: Flow&lt;NavBackStackEntry> get() = callbackFlow {"
+        errorLine2="                                                                                    ~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/navigation/NavController.kt"
+            line="40"
+            column="85"/>
+    </issue>
+
+    <issue
+        id="IllegalExperimentalApiUsage"
+        message="Experimental/OptIn APIs should only be used from within the same library or libraries within the same requireSameVersion group"
+        errorLine1="    awaitClose {"
+        errorLine2="    ~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/navigation/NavController.kt"
+            line="46"
+            column="5"/>
+    </issue>
+
+</issues>
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
index 616eea8..2f842b7 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
@@ -166,7 +166,8 @@
          * Note that this transform is applied asynchronously, as pages are loaded. Potential
          * separators between pages are only computed once both pages are loaded.
          *
-         * **Kotlin callers should instead use the extension function [insertSeparators]**
+         * **Kotlin callers should instead use the suspending extension function variant of
+         * insertSeparators**
          *
          * ```
          * /*
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
index 1708d04..77bf20d 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
@@ -272,6 +272,9 @@
         receiver?.refresh()
     }
 
+    /**
+     * @return Total number of presented items, including placeholders.
+     */
     val size: Int
         get() = presenter.size
 
@@ -299,8 +302,17 @@
      * `false` otherwise.
      */
     @Deprecated(
-        "dataRefreshFlow is now redundant with the information passed from loadStateFlow and " +
-            "getItemCount, and will be removed in a future alpha version"
+        message = "dataRefreshFlow is now redundant with the information passed from " +
+            "loadStateFlow and size(), and will be removed in a future alpha version",
+        replaceWith = ReplaceWith(
+            """loadStateFlow.map { it.source.refresh }
+                .filter { it is LoadState.NotLoading }
+                .distinctUntilChanged()""",
+            "androidx.paging.LoadState",
+            "kotlinx.coroutines.flow.distinctUntilChanged",
+            "kotlinx.coroutines.flow.filter",
+            "kotlinx.coroutines.flow.map",
+        )
     )
     @ExperimentalPagingApi
     @OptIn(FlowPreview::class)
@@ -354,7 +366,7 @@
      */
     @Deprecated(
         "dataRefreshListener is now redundant with the information passed from loadStateListener " +
-            "and getItemCount, and will be removed in a future alpha version"
+            "and size(), and will be removed in a future alpha version"
     )
     @ExperimentalPagingApi
     fun addDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
@@ -370,7 +382,7 @@
      */
     @Deprecated(
         "dataRefreshListener is now redundant with the information passed from loadStateListener " +
-            "and getItemCount, and will be removed in a future alpha version"
+            "and size(), and will be removed in a future alpha version"
     )
     @ExperimentalPagingApi
     fun removeDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
diff --git a/paging/compose/api/current.txt b/paging/compose/api/current.txt
index 075852d..e9c222a 100644
--- a/paging/compose/api/current.txt
+++ b/paging/compose/api/current.txt
@@ -5,6 +5,10 @@
     method public operator T? get(int index);
     method public int getItemCount();
     method public androidx.paging.CombinedLoadStates getLoadState();
+    method public T? peek(int index);
+    method public void refresh();
+    method public void retry();
+    method public androidx.paging.ItemSnapshotList<T> snapshot();
     property public final int itemCount;
     property public final androidx.paging.CombinedLoadStates loadState;
   }
diff --git a/paging/compose/api/public_plus_experimental_current.txt b/paging/compose/api/public_plus_experimental_current.txt
index 075852d..e9c222a 100644
--- a/paging/compose/api/public_plus_experimental_current.txt
+++ b/paging/compose/api/public_plus_experimental_current.txt
@@ -5,6 +5,10 @@
     method public operator T? get(int index);
     method public int getItemCount();
     method public androidx.paging.CombinedLoadStates getLoadState();
+    method public T? peek(int index);
+    method public void refresh();
+    method public void retry();
+    method public androidx.paging.ItemSnapshotList<T> snapshot();
     property public final int itemCount;
     property public final androidx.paging.CombinedLoadStates loadState;
   }
diff --git a/paging/compose/api/restricted_current.txt b/paging/compose/api/restricted_current.txt
index 075852d..e9c222a 100644
--- a/paging/compose/api/restricted_current.txt
+++ b/paging/compose/api/restricted_current.txt
@@ -5,6 +5,10 @@
     method public operator T? get(int index);
     method public int getItemCount();
     method public androidx.paging.CombinedLoadStates getLoadState();
+    method public T? peek(int index);
+    method public void refresh();
+    method public void retry();
+    method public androidx.paging.ItemSnapshotList<T> snapshot();
     property public final int itemCount;
     property public final androidx.paging.CombinedLoadStates loadState;
   }
diff --git a/paging/compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt b/paging/compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
index 9326d58..12200c8 100644
--- a/paging/compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
+++ b/paging/compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
@@ -28,6 +28,7 @@
 import androidx.compose.runtime.setValue
 import androidx.paging.CombinedLoadStates
 import androidx.paging.DifferCallback
+import androidx.paging.ItemSnapshotList
 import androidx.paging.LoadState
 import androidx.paging.LoadStates
 import androidx.paging.NullPaddedList
@@ -110,6 +111,58 @@
     }
 
     /**
+     * Returns the presented item at the specified position, without notifying Paging of the item
+     * access that would normally trigger page loads.
+     *
+     * @param index Index of the presented item to return, including placeholders.
+     * @return The presented item at position [index], `null` if it is a placeholder
+     */
+    fun peek(index: Int): T? {
+        return pagingDataDiffer.peek(index)
+    }
+
+    /**
+     * Returns a new [ItemSnapshotList] representing the currently presented items, including any
+     * placeholders if they are enabled.
+     */
+    fun snapshot(): ItemSnapshotList<T> {
+        return pagingDataDiffer.snapshot()
+    }
+
+    /**
+     * Retry any failed load requests that would result in a [LoadState.Error] update to this
+     * [LazyPagingItems].
+     *
+     * Unlike [refresh], this does not invalidate [PagingSource], it only retries failed loads
+     * within the same generation of [PagingData].
+     *
+     * [LoadState.Error] can be generated from two types of load requests:
+     *  * [PagingSource.load] returning [PagingSource.LoadResult.Error]
+     *  * [RemoteMediator.load] returning [RemoteMediator.MediatorResult.Error]
+     */
+    fun retry() {
+        pagingDataDiffer.retry()
+    }
+
+    /**
+     * Refresh the data presented by this [LazyPagingItems].
+     *
+     * [refresh] triggers the creation of a new [PagingData] with a new instance of [PagingSource]
+     * to represent an updated snapshot of the backing dataset. If a [RemoteMediator] is set,
+     * calling [refresh] will also trigger a call to [RemoteMediator.load] with [LoadType] [REFRESH]
+     * to allow [RemoteMediator] to check for updates to the dataset backing [PagingSource].
+     *
+     * Note: This API is intended for UI-driven refresh signals, such as swipe-to-refresh.
+     * Invalidation due repository-layer signals, such as DB-updates, should instead use
+     * [PagingSource.invalidate].
+     *
+     * @see PagingSource.invalidate
+     */
+    fun refresh() {
+        pagingDataDiffer.refresh()
+    }
+
+    /**
      * A [CombinedLoadStates] object which represents the current loading state.
      */
     public var loadState: CombinedLoadStates by mutableStateOf(
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
index cbeb45a..b1bb242 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
@@ -231,7 +231,7 @@
      * Get the number of items currently presented by this Differ. This value can be directly
      * returned to [androidx.recyclerview.widget.RecyclerView.Adapter.getItemCount].
      *
-     * @return Number of items being presented.
+     * @return Number of items being presented, including placeholders.
      */
     val itemCount: Int
         get() = differBase.size
@@ -281,8 +281,17 @@
      */
     @Suppress("DEPRECATION")
     @Deprecated(
-        "dataRefreshFlow is now redundant with the information passed from loadStateFlow and " +
-            "getItemCount, and will be removed in a future alpha version"
+        message = "dataRefreshFlow is now redundant with the information passed from " +
+            "loadStateFlow and getItemCount(), and will be removed in a future alpha version",
+        replaceWith = ReplaceWith(
+            """loadStateFlow.map { it.source.refresh }
+                .filter { it is LoadState.NotLoading }
+                .distinctUntilChanged()""",
+            "androidx.paging.LoadState",
+            "kotlinx.coroutines.flow.distinctUntilChanged",
+            "kotlinx.coroutines.flow.filter",
+            "kotlinx.coroutines.flow.map",
+        )
     )
     @ExperimentalPagingApi
     val dataRefreshFlow: Flow<Boolean> = differBase.dataRefreshFlow
diff --git a/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
index bacdc4e..c93ee7c 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
@@ -286,8 +286,17 @@
      */
     @Suppress("DEPRECATION")
     @Deprecated(
-        "dataRefreshFlow is now redundant with the information passed from loadStateFlow and " +
-            "getItemCount, and will be removed in a future alpha version"
+        message = "dataRefreshFlow is now redundant with the information passed from " +
+            "loadStateFlow and getItemCount(), and will be removed in a future alpha version",
+        replaceWith = ReplaceWith(
+            """loadStateFlow.map { it.source.refresh }
+                .filter { it is LoadState.NotLoading }
+                .distinctUntilChanged()""",
+            "androidx.paging.LoadState",
+            "kotlinx.coroutines.flow.distinctUntilChanged",
+            "kotlinx.coroutines.flow.filter",
+            "kotlinx.coroutines.flow.map",
+        )
     )
     @ExperimentalPagingApi
     val dataRefreshFlow: Flow<Boolean> = differ.dataRefreshFlow
@@ -302,7 +311,7 @@
      */
     @Deprecated(
         "dataRefreshListener is now redundant with the information passed from loadStateListener " +
-            "and getItemCount, and will be removed in a future alpha version"
+            "and getItemCount(), and will be removed in a future alpha version"
     )
     @ExperimentalPagingApi
     fun addDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
@@ -319,7 +328,7 @@
      */
     @Deprecated(
         "dataRefreshListener is now redundant with the information passed from loadStateListener " +
-            "and getItemCount, and will be removed in a future alpha version"
+            "and getItemCount(), and will be removed in a future alpha version"
     )
     @ExperimentalPagingApi
     fun removeDataRefreshListener(listener: (isEmpty: Boolean) -> Unit) {
diff --git a/recyclerview/recyclerview-benchmark/src/androidTest/AndroidManifest.xml b/recyclerview/recyclerview-benchmark/src/androidTest/AndroidManifest.xml
index 8653650..59f1491 100644
--- a/recyclerview/recyclerview-benchmark/src/androidTest/AndroidManifest.xml
+++ b/recyclerview/recyclerview-benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
             <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/room/benchmark/src/androidTest/AndroidManifest.xml b/room/benchmark/src/androidTest/AndroidManifest.xml
index 23bc3af..51a7a98 100644
--- a/room/benchmark/src/androidTest/AndroidManifest.xml
+++ b/room/benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/settings.gradle b/settings.gradle
index cc694e4..fae9aa0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -148,11 +148,12 @@
 includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin", [BuildType.MAIN])
 includeProject(":benchmark:benchmark-junit4", "benchmark/junit4")
 includeProject(":benchmark:benchmark-macro", "benchmark/macro", [BuildType.MAIN])
-includeProject(":benchmark:benchmark-macro-runtime", "benchmark/benchmark-macro-runtime", [BuildType.MAIN])
-includeProject(":benchmark:benchmark-perfetto", "benchmark/perfetto", [BuildType.MAIN])
+includeProject(":benchmark:benchmark-macro-runtime", "benchmark/benchmark-macro-runtime", [BuildType.MAIN, BuildType.COMPOSE])
+includeProject(":benchmark:benchmark-perfetto", "benchmark/perfetto", [BuildType.MAIN, BuildType.COMPOSE])
+includeProject(":benchmark:benchmark-simple-macro-benchmark", "benchmark/benchmark-simple-macro-benchmark", [BuildType.MAIN])
 includeProject(":benchmark:integration-tests:dry-run-benchmark", "benchmark/integration-tests/dry-run-benchmark", [BuildType.MAIN])
 includeProject(":benchmark:integration-tests:startup-benchmark", "benchmark/integration-tests/startup-benchmark", [BuildType.MAIN])
-includeProject(":benchmark:integration-tests:macro-benchmark-target", "benchmark/integration-tests/macro-benchmark-target", [BuildType.MAIN])
+includeProject(":benchmark:integration-tests:benchmark-simple-macro-benchmark-target", "benchmark/integration-tests/benchmark-simple-macro-benchmark-target", [BuildType.MAIN])
 includeProject(":biometric:biometric", "biometric/biometric", [BuildType.MAIN])
 includeProject(":biometric:integration-tests:testapp", "biometric/integration-tests/testapp", [BuildType.MAIN])
 includeProject(":browser:browser", "browser/browser", [BuildType.MAIN])
@@ -250,7 +251,6 @@
 includeProject(":compose:ui:ui-test-font", "compose/ui/ui-test-font", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-test-junit4", "compose/ui/ui-test-junit4", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-text", "compose/ui/ui-text", [BuildType.COMPOSE])
-includeProject(":compose:ui:ui-text-android", "compose/ui/ui-text-android", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-text:ui-text-samples", "compose/ui/ui-text/samples", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-unit", "compose/ui/ui-unit", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-unit:ui-unit-samples", "compose/ui/ui-unit/samples", [BuildType.COMPOSE])
@@ -464,6 +464,7 @@
 includeProject(":swiperefreshlayout:swiperefreshlayout", "swiperefreshlayout/swiperefreshlayout", [BuildType.MAIN])
 includeProject(":test-screenshot", "test/screenshot")
 includeProject(":test-screenshot-proto", "test/screenshot/proto", [BuildType.MAIN])
+includeProject(":text:text", "text/text", [BuildType.COMPOSE])
 includeProject(":textclassifier:integration-tests:testapp", "textclassifier/integration-tests/testapp", [BuildType.MAIN])
 includeProject(":textclassifier:textclassifier", "textclassifier/textclassifier", [BuildType.MAIN])
 includeProject(":tracing:tracing", "tracing/tracing")
diff --git a/slices/benchmark/src/androidTest/AndroidManifest.xml b/slices/benchmark/src/androidTest/AndroidManifest.xml
index 8e2298b..18c89a7 100644
--- a/slices/benchmark/src/androidTest/AndroidManifest.xml
+++ b/slices/benchmark/src/androidTest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+            android:requestLegacyExternalStorage="true"
             android:debuggable="false"
             tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->
diff --git a/compose/ui/ui-text-android/OWNERS b/text/text/OWNERS
similarity index 100%
rename from compose/ui/ui-text-android/OWNERS
rename to text/text/OWNERS
diff --git a/compose/ui/ui-text-android/build.gradle b/text/text/build.gradle
similarity index 87%
rename from compose/ui/ui-text-android/build.gradle
rename to text/text/build.gradle
index 4906867..3f5f325 100644
--- a/compose/ui/ui-text-android/build.gradle
+++ b/text/text/build.gradle
@@ -52,11 +52,11 @@
 }
 
 androidx {
-    name = "Compose UI Text Android support"
-    publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.COMPOSE
-    mavenGroup = LibraryGroups.Compose.UI
-    inceptionYear = "2018"
-    description = "Android specific implementations for Text in Compose"
+    name = "UI Text utilities"
+    publish = Publish.NONE
+    mavenVersion = LibraryVersions.TEXT
+    mavenGroup = LibraryGroups.TEXT
+    inceptionYear = "2020"
+    description = "Text primitives and utilities"
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/compose/ui/ui-text-android/lint-baseline.xml b/text/text/lint-baseline.xml
similarity index 100%
rename from compose/ui/ui-text-android/lint-baseline.xml
rename to text/text/lint-baseline.xml
diff --git a/compose/ui/ui-text-android/src/androidTest/AndroidManifest.xml b/text/text/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/AndroidManifest.xml
rename to text/text/src/androidTest/AndroidManifest.xml
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontWeightStyleSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontWeightStyleSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/FontWeightStyleSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontWeightStyleSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
rename to text/text/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
diff --git a/compose/ui/ui-text-android/src/main/AndroidManifest.xml b/text/text/src/main/AndroidManifest.xml
similarity index 100%
rename from compose/ui/ui-text-android/src/main/AndroidManifest.xml
rename to text/text/src/main/AndroidManifest.xml
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt b/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.kt b/text/text/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.kt b/text/text/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt b/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/TextLayout.kt b/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt b/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.kt b/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt b/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.kt b/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/FontSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/FontSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/FontWeightStyleSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
diff --git a/compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
similarity index 100%
rename from compose/ui/ui-text-android/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
rename to text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
diff --git a/ui/ui-tooling/api/current.txt b/ui/ui-tooling/api/current.txt
index 3a99d46..e04894b 100644
--- a/ui/ui-tooling/api/current.txt
+++ b/ui/ui-tooling/api/current.txt
@@ -113,7 +113,7 @@
     method public String getName();
     method public int getOffset();
     method public int getPackageHash();
-    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> getParameters();
+    method public java.util.List<androidx.ui.tooling.inspector.RawParameter> getParameters();
     method public int getTop();
     method public int getWidth();
     property public final java.util.List<androidx.ui.tooling.inspector.InspectorNode> children;
@@ -126,7 +126,7 @@
     property public final String name;
     property public final int offset;
     property public final int packageHash;
-    property public final java.util.List<androidx.ui.tooling.inspector.NodeParameter> parameters;
+    property public final java.util.List<androidx.ui.tooling.inspector.RawParameter> parameters;
     property public final int top;
     property public final int width;
   }
@@ -134,6 +134,8 @@
   public final class LayoutInspectorTree {
     ctor public LayoutInspectorTree();
     method public java.util.List<androidx.ui.tooling.inspector.InspectorNode> convert(android.view.View view);
+    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> convertParameters(androidx.ui.tooling.inspector.InspectorNode node);
+    method public void resetGeneratedId();
   }
 
   public final class LayoutInspectorTreeKt {
@@ -167,6 +169,14 @@
     enum_constant public static final androidx.ui.tooling.inspector.ParameterType String;
   }
 
+  public final class RawParameter {
+    ctor public RawParameter(String name, Object? value);
+    method public String getName();
+    method public Object? getValue();
+    property public final String name;
+    property public final Object? value;
+  }
+
 }
 
 package androidx.ui.tooling.preview {
diff --git a/ui/ui-tooling/api/public_plus_experimental_current.txt b/ui/ui-tooling/api/public_plus_experimental_current.txt
index 3a99d46..e04894b 100644
--- a/ui/ui-tooling/api/public_plus_experimental_current.txt
+++ b/ui/ui-tooling/api/public_plus_experimental_current.txt
@@ -113,7 +113,7 @@
     method public String getName();
     method public int getOffset();
     method public int getPackageHash();
-    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> getParameters();
+    method public java.util.List<androidx.ui.tooling.inspector.RawParameter> getParameters();
     method public int getTop();
     method public int getWidth();
     property public final java.util.List<androidx.ui.tooling.inspector.InspectorNode> children;
@@ -126,7 +126,7 @@
     property public final String name;
     property public final int offset;
     property public final int packageHash;
-    property public final java.util.List<androidx.ui.tooling.inspector.NodeParameter> parameters;
+    property public final java.util.List<androidx.ui.tooling.inspector.RawParameter> parameters;
     property public final int top;
     property public final int width;
   }
@@ -134,6 +134,8 @@
   public final class LayoutInspectorTree {
     ctor public LayoutInspectorTree();
     method public java.util.List<androidx.ui.tooling.inspector.InspectorNode> convert(android.view.View view);
+    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> convertParameters(androidx.ui.tooling.inspector.InspectorNode node);
+    method public void resetGeneratedId();
   }
 
   public final class LayoutInspectorTreeKt {
@@ -167,6 +169,14 @@
     enum_constant public static final androidx.ui.tooling.inspector.ParameterType String;
   }
 
+  public final class RawParameter {
+    ctor public RawParameter(String name, Object? value);
+    method public String getName();
+    method public Object? getValue();
+    property public final String name;
+    property public final Object? value;
+  }
+
 }
 
 package androidx.ui.tooling.preview {
diff --git a/ui/ui-tooling/api/restricted_current.txt b/ui/ui-tooling/api/restricted_current.txt
index 3a99d46..e04894b 100644
--- a/ui/ui-tooling/api/restricted_current.txt
+++ b/ui/ui-tooling/api/restricted_current.txt
@@ -113,7 +113,7 @@
     method public String getName();
     method public int getOffset();
     method public int getPackageHash();
-    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> getParameters();
+    method public java.util.List<androidx.ui.tooling.inspector.RawParameter> getParameters();
     method public int getTop();
     method public int getWidth();
     property public final java.util.List<androidx.ui.tooling.inspector.InspectorNode> children;
@@ -126,7 +126,7 @@
     property public final String name;
     property public final int offset;
     property public final int packageHash;
-    property public final java.util.List<androidx.ui.tooling.inspector.NodeParameter> parameters;
+    property public final java.util.List<androidx.ui.tooling.inspector.RawParameter> parameters;
     property public final int top;
     property public final int width;
   }
@@ -134,6 +134,8 @@
   public final class LayoutInspectorTree {
     ctor public LayoutInspectorTree();
     method public java.util.List<androidx.ui.tooling.inspector.InspectorNode> convert(android.view.View view);
+    method public java.util.List<androidx.ui.tooling.inspector.NodeParameter> convertParameters(androidx.ui.tooling.inspector.InspectorNode node);
+    method public void resetGeneratedId();
   }
 
   public final class LayoutInspectorTreeKt {
@@ -167,6 +169,14 @@
     enum_constant public static final androidx.ui.tooling.inspector.ParameterType String;
   }
 
+  public final class RawParameter {
+    ctor public RawParameter(String name, Object? value);
+    method public String getName();
+    method public Object? getValue();
+    property public final String name;
+    property public final Object? value;
+  }
+
 }
 
 package androidx.ui.tooling.preview {
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
index 48ad3aa..4fbfa86 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
@@ -93,9 +93,9 @@
         dumpSlotTableSet(slotTableRecord)
         val builder = LayoutInspectorTree()
         val nodes = builder.convert(view)
-        dumpNodes(nodes)
+        dumpNodes(nodes, builder)
 
-        validate(nodes, checkParameters = true) {
+        validate(nodes, builder, checkParameters = true) {
             node(
                 name = "Box",
                 fileName = "Box.kt",
@@ -404,10 +404,10 @@
         dumpSlotTableSet(slotTableRecord)
         val builder = LayoutInspectorTree()
         val nodes = builder.convert(view)
-        dumpNodes(nodes)
+        dumpNodes(nodes, builder)
 
         if (DEBUG) {
-            validate(nodes, checkParameters = false) {
+            validate(nodes, builder, checkParameters = false) {
                 node("Box", children = listOf("ModalDrawerLayout"))
                 node("ModalDrawerLayout", children = listOf("WithConstraints"))
                 node("WithConstraints", children = listOf("SubcomposeLayout"))
@@ -484,18 +484,20 @@
 
     private fun validate(
         result: List<InspectorNode>,
+        builder: LayoutInspectorTree,
         checkParameters: Boolean,
         block: TreeValidationReceiver.() -> Unit = {}
     ) {
         val nodes = result.flatMap { flatten(it) }.iterator()
-        val tree = TreeValidationReceiver(nodes, density, checkParameters)
+        val tree = TreeValidationReceiver(nodes, density, checkParameters, builder)
         tree.block()
     }
 
     private class TreeValidationReceiver(
         val nodeIterator: Iterator<InspectorNode>,
         val density: Density,
-        val checkParameters: Boolean
+        val checkParameters: Boolean,
+        val builder: LayoutInspectorTree
     ) {
         fun node(
             name: String,
@@ -534,11 +536,12 @@
             }
 
             if (checkParameters) {
-                val params = ParameterValidationReceiver(node.parameters.listIterator())
-                params.block()
-                if (params.parameterIterator.hasNext()) {
+                val params = builder.convertParameters(node)
+                val receiver = ParameterValidationReceiver(params.listIterator())
+                receiver.block()
+                if (receiver.parameterIterator.hasNext()) {
                     val elementNames = mutableListOf<String>()
-                    params.parameterIterator.forEachRemaining { elementNames.add(it.name) }
+                    receiver.parameterIterator.forEachRemaining { elementNames.add(it.name) }
                     error("$name: has more parameters like: ${elementNames.joinToString()}")
                 }
             }
@@ -549,7 +552,7 @@
         listOf(node).plus(node.children.flatMap { flatten(it) })
 
     // region DEBUG print methods
-    private fun dumpNodes(nodes: List<InspectorNode>) {
+    private fun dumpNodes(nodes: List<InspectorNode>, builder: LayoutInspectorTree) {
         @Suppress("ConstantConditionIf")
         if (!DEBUG) {
             return
@@ -559,7 +562,7 @@
         nodes.forEach { dumpNode(it, indent = 0) }
         println()
         println("=================== validate statements ==========================")
-        nodes.forEach { generateValidate(it) }
+        nodes.forEach { generateValidate(it, builder) }
     }
 
     private fun dumpNode(node: InspectorNode, indent: Int) {
@@ -571,7 +574,7 @@
         node.children.forEach { dumpNode(it, indent + 1) }
     }
 
-    private fun generateValidate(node: InspectorNode) {
+    private fun generateValidate(node: InspectorNode, builder: LayoutInspectorTree) {
         with(density) {
             val left = round(node.left.toDp())
             val top = round(node.top.toDp())
@@ -599,10 +602,10 @@
         println()
         print(")")
         if (node.parameters.isNotEmpty()) {
-            generateParameters(node.parameters, 0)
+            generateParameters(builder.convertParameters(node), 0)
         }
         println()
-        node.children.forEach { generateValidate(it) }
+        node.children.forEach { generateValidate(it, builder) }
     }
 
     private fun generateParameters(parameters: List<NodeParameter>, indent: Int) {
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/ParameterFactoryTest.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/ParameterFactoryTest.kt
index 73473ae..200a1ad 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/ParameterFactoryTest.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/ParameterFactoryTest.kt
@@ -82,15 +82,16 @@
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 class ParameterFactoryTest {
-    private val node = MutableInspectorNode()
     private val factory = ParameterFactory(InlineClassConverter())
     private val api = android.os.Build.VERSION.SDK_INT
+    private val node = MutableInspectorNode().apply {
+        width = 1000
+        height = 500
+    }.build()
 
     @Before
     fun before() {
         factory.density = Density(2.0f)
-        node.width = 1000
-        node.height = 500
         isDebugInspectorInfoEnabled = true
     }
 
diff --git a/ui/ui-tooling/src/main/AndroidManifest.xml b/ui/ui-tooling/src/main/AndroidManifest.xml
index 9529f2a..e4acc4a 100644
--- a/ui/ui-tooling/src/main/AndroidManifest.xml
+++ b/ui/ui-tooling/src/main/AndroidManifest.xml
@@ -19,8 +19,7 @@
     <application>
         <activity
             android:name=".preview.PreviewActivity"
-            android:exported="true"
-            android:theme="@android:style/Theme.Material.Light.NoActionBar" />
+            android:exported="true" />
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/InspectorNode.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/InspectorNode.kt
index f933d53..d495e31 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/InspectorNode.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/InspectorNode.kt
@@ -89,7 +89,7 @@
     /**
      * The parameters of this Composable.
      */
-    val parameters: List<NodeParameter>,
+    val parameters: List<RawParameter>,
 
     /**
      * The children nodes of this Composable.
@@ -98,6 +98,11 @@
 )
 
 /**
+ * Parameter definition with a raw value reference.
+ */
+class RawParameter(val name: String, val value: Any?)
+
+/**
  * Mutable version of [InspectorNode].
  */
 @ExperimentalLayoutNodeApi
@@ -114,7 +119,7 @@
     var top = 0
     var width = 0
     var height = 0
-    val parameters = mutableListOf<NodeParameter>()
+    val parameters = mutableListOf<RawParameter>()
     val children = mutableListOf<InspectorNode>()
 
     fun reset() {
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
index 792b150..a6dc0eb 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
@@ -89,10 +89,23 @@
         return result
     }
 
+    /**
+     * Converts the [RawParameter]s of the [node] into displayable parameters.
+     */
+    fun convertParameters(node: InspectorNode): List<NodeParameter> {
+        return node.parameters.mapNotNull { parameterFactory.create(node, it.name, it.value) }
+    }
+
+    /**
+     * Reset the generated id. Nodes are assigned an id if there isn't a layout node id present.
+     */
+    fun resetGeneratedId() {
+        generatedId = -1L
+    }
+
     private fun clear() {
         cache.clear()
         inlineClassConverter.clear()
-        generatedId = -1L
         claimedNodes.clear()
         treeMap.clear()
         ownerMap.clear()
@@ -298,8 +311,8 @@
         parameters.forEach { addParameter(it, node) }
 
     private fun addParameter(parameter: ParameterInformation, node: MutableInspectorNode) {
-        val castedValue = castValue(parameter) ?: return
-        parameterFactory.create(node, parameter.name, castedValue)?.let { node.parameters.add(it) }
+        val castedValue = castValue(parameter)
+        node.parameters.add(RawParameter(parameter.name, castedValue))
     }
 
     private fun castValue(parameter: ParameterInformation): Any? {
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/ParameterFactory.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/ParameterFactory.kt
index 2d0a0347..cc12a73 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/ParameterFactory.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/ParameterFactory.kt
@@ -17,7 +17,9 @@
 package androidx.ui.tooling.inspector
 
 import android.util.Log
+import android.view.View
 import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.ui.AbsoluteAlignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
@@ -97,6 +99,10 @@
         valueLookup[Color.Unspecified] = "Unspecified"
         valuesLoaded.add(Enum::class.java)
         valuesLoaded.add(Any::class.java)
+
+        // AbsoluteAlignment is not found from an instance of BiasAbsoluteAlignment,
+        // because Alignment has no file level class.
+        loadConstantsFromEnclosedClasses(AbsoluteAlignment::class.java)
     }
 
     /**
@@ -105,7 +111,7 @@
      * Attempt to convert the value to a user readable value.
      * For now: return null when a conversion is not possible/found.
      */
-    fun create(node: MutableInspectorNode, name: String, value: Any?): NodeParameter? {
+    fun create(node: InspectorNode, name: String, value: Any?): NodeParameter? {
         val creator = creatorCache ?: ParameterCreator()
         try {
             return creator.create(node, name, value)
@@ -233,10 +239,10 @@
      * Convenience class for building [NodeParameter]s.
      */
     private inner class ParameterCreator {
-        private var node: MutableInspectorNode? = null
+        private var node: InspectorNode? = null
         private var recursions = 0
 
-        fun create(node: MutableInspectorNode, name: String, value: Any?): NodeParameter? = try {
+        fun create(node: InspectorNode, name: String, value: Any?): NodeParameter? = try {
             this.node = node
             recursions = 0
             create(name, value)
@@ -277,6 +283,7 @@
                     is String -> NodeParameter(name, ParameterType.String, value)
                     is TextUnit -> createFromTextUnit(name, value)
                     is VectorAsset -> createFromVectorAssert(name, value)
+                    is View -> NodeParameter(name, ParameterType.String, value.javaClass.simpleName)
                     else -> createFromKotlinReflection(name, value)
                 }
             } finally {
diff --git a/wear/wear-complications-data/api/restricted_current.txt b/wear/wear-complications-data/api/restricted_current.txt
index 8f62011..767e483 100644
--- a/wear/wear-complications-data/api/restricted_current.txt
+++ b/wear/wear-complications-data/api/restricted_current.txt
@@ -286,6 +286,7 @@
   }
 
   public enum ComplicationType {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final java.util.List<androidx.wear.complications.data.ComplicationType> fromWireTypeList(int[] types);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.wear.complications.data.ComplicationType![] fromWireTypes(int[] types);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final int[] toWireTypes(java.util.Collection<? extends androidx.wear.complications.data.ComplicationType> types);
     enum_constant public static final androidx.wear.complications.data.ComplicationType BACKGROUND_IMAGE;
@@ -302,6 +303,7 @@
   }
 
   public static final class ComplicationType.Companion {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.List<androidx.wear.complications.data.ComplicationType> fromWireTypeList(int[] types);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.wear.complications.data.ComplicationType![] fromWireTypes(int[] types);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public int[] toWireTypes(java.util.Collection<? extends androidx.wear.complications.data.ComplicationType> types);
   }
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/data/Type.kt b/wear/wear-complications-data/src/main/java/androidx/wear/complications/data/Type.kt
index 8dea102..d460dc6 100644
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/data/Type.kt
+++ b/wear/wear-complications-data/src/main/java/androidx/wear/complications/data/Type.kt
@@ -81,8 +81,8 @@
         public fun toWireTypes(types: Collection<ComplicationType>): IntArray = types.asWireTypes()
 
         /**
-         * Converts an array of integer values uses for serialization into the corresponding array
-         * of [ComplicationType] to .
+         * Converts an array of integer values used for serialization into the corresponding array
+         * of [ComplicationType].
          *
          * This is only needed internally to convert to the underlying communication protocol.
          *
@@ -94,6 +94,17 @@
         @JvmStatic
         public fun fromWireTypes(types: IntArray): Array<ComplicationType> =
             types.asApiComplicationTypes()
+
+        /**
+         * Converts an array of integer values used for serialization into the corresponding list
+         * of [ComplicationType].
+         *
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @JvmStatic
+        public fun fromWireTypeList(types: IntArray): List<ComplicationType> =
+            types.map { fromWireType(it) }
     }
 }
 
diff --git a/wear/wear-watchface-client/api/current.txt b/wear/wear-watchface-client/api/current.txt
index 2484c5d..4a645c1 100644
--- a/wear/wear-watchface-client/api/current.txt
+++ b/wear/wear-watchface-client/api/current.txt
@@ -1,14 +1,31 @@
 // Signature format: 4.0
 package androidx.wear.watchface.client {
 
+  public final class ComplicationState {
+    ctor public ComplicationState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.data.ComplicationType defaultProviderType, boolean isEnabled);
+    method public android.graphics.Rect getBounds();
+    method public int getBoundsType();
+    method public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
+    method public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
+    method public boolean isEnabled();
+    property public final android.graphics.Rect bounds;
+    property public final int boundsType;
+    property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
+    property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
+    property public final boolean isEnabled;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
+  }
+
   public final class DeviceConfig {
-    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, @androidx.wear.watchface.ScreenShape int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public int getScreenShape();
     method public boolean hasBurnInProtection();
     method public boolean hasLowBitAmbient();
-    method public void setHasBurnInProtection(boolean p);
-    method public void setHasLowBitAmbient(boolean p);
-    method public void setScreenShape(int p);
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final int screenShape;
@@ -18,12 +35,12 @@
     ctor public HeadlessWatchFaceClient(android.os.IBinder binder);
     method public android.os.IBinder asBinder();
     method public void close();
-    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> getComplicationDetails();
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
     method public android.graphics.Bitmap? takeComplicationScreenshot(int complicationId, androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.complications.data.ComplicationData complicationData, androidx.wear.watchface.style.UserStyle? userStyle);
     method public android.graphics.Bitmap takeWatchFaceScreenshot(androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
-    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> complicationDetails;
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
     property public final long previewReferenceTimeMillis;
     property public final androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
   }
diff --git a/wear/wear-watchface-client/api/public_plus_experimental_current.txt b/wear/wear-watchface-client/api/public_plus_experimental_current.txt
index 2484c5d..96dd3c9 100644
--- a/wear/wear-watchface-client/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-client/api/public_plus_experimental_current.txt
@@ -1,14 +1,31 @@
 // Signature format: 4.0
 package androidx.wear.watchface.client {
 
+  public final class ComplicationState {
+    ctor public ComplicationState(android.graphics.Rect bounds, @androidx.wear.watchface.data.ComplicationBoundsType int boundsType, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.data.ComplicationType defaultProviderType, boolean isEnabled);
+    method public android.graphics.Rect getBounds();
+    method public int getBoundsType();
+    method public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
+    method public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
+    method public boolean isEnabled();
+    property public final android.graphics.Rect bounds;
+    property public final int boundsType;
+    property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
+    property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
+    property public final boolean isEnabled;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
+  }
+
   public final class DeviceConfig {
-    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, @androidx.wear.watchface.ScreenShape int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public int getScreenShape();
     method public boolean hasBurnInProtection();
     method public boolean hasLowBitAmbient();
-    method public void setHasBurnInProtection(boolean p);
-    method public void setHasLowBitAmbient(boolean p);
-    method public void setScreenShape(int p);
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final int screenShape;
@@ -18,12 +35,12 @@
     ctor public HeadlessWatchFaceClient(android.os.IBinder binder);
     method public android.os.IBinder asBinder();
     method public void close();
-    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> getComplicationDetails();
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
     method public android.graphics.Bitmap? takeComplicationScreenshot(int complicationId, androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.complications.data.ComplicationData complicationData, androidx.wear.watchface.style.UserStyle? userStyle);
     method public android.graphics.Bitmap takeWatchFaceScreenshot(androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
-    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> complicationDetails;
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
     property public final long previewReferenceTimeMillis;
     property public final androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
   }
diff --git a/wear/wear-watchface-client/api/restricted_current.txt b/wear/wear-watchface-client/api/restricted_current.txt
index 12409fc..1cd2c82 100644
--- a/wear/wear-watchface-client/api/restricted_current.txt
+++ b/wear/wear-watchface-client/api/restricted_current.txt
@@ -1,14 +1,31 @@
 // Signature format: 4.0
 package androidx.wear.watchface.client {
 
+  public final class ComplicationState {
+    ctor public ComplicationState(android.graphics.Rect bounds, @androidx.wear.watchface.data.ComplicationBoundsType int boundsType, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.data.ComplicationType defaultProviderType, boolean isEnabled);
+    method public android.graphics.Rect getBounds();
+    method public int getBoundsType();
+    method public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
+    method public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
+    method public boolean isEnabled();
+    property public final android.graphics.Rect bounds;
+    property public final int boundsType;
+    property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
+    property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
+    property public final boolean isEnabled;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
+  }
+
   public final class DeviceConfig {
-    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public DeviceConfig(boolean hasLowBitAmbient, boolean hasBurnInProtection, @androidx.wear.watchface.ScreenShape int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public int getScreenShape();
     method public boolean hasBurnInProtection();
     method public boolean hasLowBitAmbient();
-    method public void setHasBurnInProtection(boolean p);
-    method public void setHasLowBitAmbient(boolean p);
-    method public void setScreenShape(int p);
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final int screenShape;
@@ -18,12 +35,12 @@
     ctor public HeadlessWatchFaceClient(android.os.IBinder binder);
     method public android.os.IBinder asBinder();
     method public void close();
-    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> getComplicationDetails();
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
     method public android.graphics.Bitmap? takeComplicationScreenshot(int complicationId, androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.complications.data.ComplicationData complicationData, androidx.wear.watchface.style.UserStyle? userStyle);
     method public android.graphics.Bitmap takeWatchFaceScreenshot(androidx.wear.watchface.RenderParameters renderParameters, @IntRange(from=0, to=100) int compressionQuality, long calendarTimeMillis, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
-    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.data.ComplicationDetails> complicationDetails;
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
     property public final long previewReferenceTimeMillis;
     property public final androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
   }
diff --git a/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 2142ebb..f73a92b 100644
--- a/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -19,20 +19,26 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.graphics.Rect
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import androidx.test.screenshot.assertAgainstGolden
+import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.data.ComplicationText
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.complications.data.ShortTextComplicationData
 import androidx.wear.watchface.DrawMode
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.client.DeviceConfig
 import androidx.wear.watchface.client.WatchFaceControlClient
 import androidx.wear.watchface.control.WatchFaceControlService
+import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,6 +60,19 @@
     @get:Rule
     val screenshotRule = AndroidXScreenshotTestRule("wear/wear-watchface-client")
 
+    private val exampleWatchFaceComponentName = ComponentName(
+        "androidx.wear.watchface.samples.test",
+        "androidx.wear.watchface.samples.ExampleCanvasWatchFaceService"
+    )
+
+    private val deviceConfig = DeviceConfig(
+        false,
+        false,
+        1,
+        0,
+        0
+    )
+
     @Test
     fun headlessScreenshot() {
         val headlessInstance = service.createHeadlessWatchFaceClient(
@@ -65,6 +84,8 @@
                 false,
                 false,
                 1,
+                0,
+                0
             ),
             400,
             400
@@ -91,4 +112,59 @@
         headlessInstance.close()
         service.close()
     }
+
+    @Test
+    fun complicationDetails() {
+        val headlessInstance = service.createHeadlessWatchFaceClient(
+            exampleWatchFaceComponentName,
+            deviceConfig,
+            400,
+            400
+        ).get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
+
+        assertThat(headlessInstance.complicationState.size).isEqualTo(2)
+
+        val leftComplicationDetails = headlessInstance.complicationState[
+            EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
+        ]!!
+        assertThat(leftComplicationDetails.bounds).isEqualTo(Rect(80, 160, 160, 240))
+        assertThat(leftComplicationDetails.boundsType).isEqualTo(ComplicationBoundsType.ROUND_RECT)
+        assertThat(leftComplicationDetails.defaultProviderPolicy.systemProviderFallback).isEqualTo(
+            SystemProviders.DAY_OF_WEEK
+        )
+        assertThat(leftComplicationDetails.defaultProviderType).isEqualTo(
+            ComplicationType.SHORT_TEXT
+        )
+        assertThat(leftComplicationDetails.supportedTypes).containsExactly(
+            ComplicationType.RANGED_VALUE,
+            ComplicationType.LONG_TEXT,
+            ComplicationType.SHORT_TEXT,
+            ComplicationType.MONOCHROMATIC_IMAGE,
+            ComplicationType.SMALL_IMAGE
+        )
+        assertTrue(leftComplicationDetails.isEnabled)
+
+        val rightComplicationDetails = headlessInstance.complicationState[
+            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+        ]!!
+        assertThat(rightComplicationDetails.bounds).isEqualTo(Rect(240, 160, 320, 240))
+        assertThat(rightComplicationDetails.boundsType).isEqualTo(ComplicationBoundsType.ROUND_RECT)
+        assertThat(rightComplicationDetails.defaultProviderPolicy.systemProviderFallback).isEqualTo(
+            SystemProviders.STEP_COUNT
+        )
+        assertThat(rightComplicationDetails.defaultProviderType).isEqualTo(
+            ComplicationType.SHORT_TEXT
+        )
+        assertThat(rightComplicationDetails.supportedTypes).containsExactly(
+            ComplicationType.RANGED_VALUE,
+            ComplicationType.LONG_TEXT,
+            ComplicationType.SHORT_TEXT,
+            ComplicationType.MONOCHROMATIC_IMAGE,
+            ComplicationType.SMALL_IMAGE
+        )
+        assertTrue(rightComplicationDetails.isEnabled)
+
+        headlessInstance.close()
+        service.close()
+    }
 }
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationState.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationState.kt
new file mode 100644
index 0000000..889a3a5
--- /dev/null
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationState.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.client
+
+import android.graphics.Rect
+import androidx.wear.complications.DefaultComplicationProviderPolicy
+import androidx.wear.complications.data.ComplicationType
+import androidx.wear.watchface.Complication
+import androidx.wear.watchface.data.ComplicationBoundsType
+import androidx.wear.watchface.data.ComplicationStateWireFormat
+
+/** State of the [Complication]. */
+public class ComplicationState(
+    /** Screen space bounds of the [Complication] in pixels. */
+    public val bounds: Rect,
+
+    /** The type of the complication's bounds. */
+    @ComplicationBoundsType public val boundsType: Int,
+
+    /** The [ComplicationType]s supported for this complication. */
+    public val supportedTypes: List<ComplicationType>,
+
+    /** The [DefaultComplicationProviderPolicy] for this complication. */
+    public val defaultProviderPolicy: DefaultComplicationProviderPolicy,
+
+    /** The default [ComplicationType] for this complication. */
+    public val defaultProviderType: ComplicationType,
+
+    /** Whether or not the complication is drawn. */
+    @get:JvmName("isEnabled")
+    public val isEnabled: Boolean
+) {
+    internal constructor(
+        complicationStateWireFormat: ComplicationStateWireFormat
+    ) : this(
+        complicationStateWireFormat.bounds,
+        complicationStateWireFormat.boundsType,
+        complicationStateWireFormat.supportedTypes.map { ComplicationType.fromWireType(it) },
+        DefaultComplicationProviderPolicy(
+            complicationStateWireFormat.defaultProvidersToTry ?: emptyList(),
+            complicationStateWireFormat.fallbackSystemProvider
+        ),
+        ComplicationType.fromWireType(complicationStateWireFormat.defaultProviderType),
+        complicationStateWireFormat.isEnabled
+    )
+}
\ No newline at end of file
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/DeviceConfig.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/DeviceConfig.kt
index 771d42d..c929560 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/DeviceConfig.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/DeviceConfig.kt
@@ -16,37 +16,29 @@
 
 package androidx.wear.watchface.client
 
-import androidx.annotation.IntDef
-
-/** @hide */
-@IntDef(
-    value = [
-        ScreenShape.ROUND,
-        ScreenShape.RECTANGULAR
-    ]
-)
-public annotation class ScreenShape {
-    public companion object {
-        /** The watch screen has a circular shape. */
-        public const val ROUND: Int = androidx.wear.watchface.data.DeviceConfig.SCREEN_SHAPE_ROUND
-
-        /** The watch screen has a rectangular or square shape. */
-        public const val RECTANGULAR: Int =
-            androidx.wear.watchface.data.DeviceConfig.SCREEN_SHAPE_RECTANGULAR
-    }
-}
+import androidx.wear.watchface.ScreenShape
 
 /** Describes the hardware configuration of the device the watch face is running on. */
 public class DeviceConfig(
     /** Whether or not the watch hardware supports low bit ambient support. */
     @get:JvmName("hasLowBitAmbient")
-    public var hasLowBitAmbient: Boolean,
+    public val hasLowBitAmbient: Boolean,
 
     /** Whether or not the watch hardware supports burn in protection. */
     @get:JvmName("hasBurnInProtection")
-    public var hasBurnInProtection: Boolean,
+    public val hasBurnInProtection: Boolean,
 
     /** Describes the shape of the screen of the device the watch face is running on.*/
     @ScreenShape
-    public var screenShape: Int
+    public val screenShape: Int,
+
+    /**
+     * UTC reference time for screenshots of analog watch faces in milliseconds since the epoch.
+     */
+    public val analogPreviewReferenceTimeMillis: Long,
+
+    /**
+     * UTC reference time for screenshots of digital watch faces in milliseconds since the epoch.
+     */
+    public val digitalPreviewReferenceTimeMillis: Long
 )
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
index 4d7cb92..622d5dd 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
@@ -25,7 +25,6 @@
 import androidx.wear.watchface.control.IHeadlessWatchFace
 import androidx.wear.watchface.control.data.ComplicationScreenshotParams
 import androidx.wear.watchface.control.data.WatchfaceScreenshotParams
-import androidx.wear.watchface.data.ComplicationDetails
 import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.UserStyleSchema
@@ -38,7 +37,7 @@
     /** Constructs a [HeadlessWatchFaceClient] from a [IBinder]. */
     public constructor(binder: IBinder) : this(IHeadlessWatchFace.Stub.asInterface(binder))
 
-    /** The reference preview time for this watch face in milliseconds since the epoch. */
+    /** The UTC reference preview time for this watch face in milliseconds since the epoch. */
     public val previewReferenceTimeMillis: Long
         get() = iHeadlessWatchFace.previewReferenceTimeMillis
 
@@ -47,13 +46,13 @@
         get() = UserStyleSchema(iHeadlessWatchFace.userStyleSchema)
 
     /**
-     * Map of complication ids to [ComplicationDetails] for each complication slot. Note this can
+     * Map of complication ids to [ComplicationState] for each complication slot. Note this can
      * change, typically in response to styling.
      */
-    public val complicationDetails: Map<Int, ComplicationDetails>
-        get() = iHeadlessWatchFace.complicationDetails.associateBy(
+    public val complicationState: Map<Int, ComplicationState>
+        get() = iHeadlessWatchFace.complicationState.associateBy(
             { it.id },
-            { it.complicationDetails }
+            { ComplicationState(it.complicationState) }
         )
 
     /**
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceSysUiClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceSysUiClient.kt
index 66eddb3..def813a 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceSysUiClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceSysUiClient.kt
@@ -127,7 +127,7 @@
         )
     ).ashmemCompressedImageBundleToBitmap()
 
-    /** The reference preview time for this watch face in milliseconds since the epoch. */
+    /** The UTC reference preview time for this watch face in milliseconds since the epoch. */
     public val previewReferenceTimeMillis: Long
         get() = iInteractiveWatchFaceSysUI.previewReferenceTimeMillis
 
@@ -157,4 +157,4 @@
     override fun close() {
         iInteractiveWatchFaceSysUI.release()
     }
-}
\ No newline at end of file
+}
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index 3d669f7..65a84a1 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -143,7 +143,9 @@
                                     androidx.wear.watchface.data.DeviceConfig(
                                         deviceConfig.hasLowBitAmbient,
                                         deviceConfig.hasBurnInProtection,
-                                        deviceConfig.screenShape
+                                        deviceConfig.screenShape,
+                                        deviceConfig.analogPreviewReferenceTimeMillis,
+                                        deviceConfig.digitalPreviewReferenceTimeMillis
                                     ),
                                     surfaceWidth,
                                     surfaceHeight
diff --git a/wear/wear-watchface-data/api/restricted_current.txt b/wear/wear-watchface-data/api/restricted_current.txt
index a21043c..7088f70 100644
--- a/wear/wear-watchface-data/api/restricted_current.txt
+++ b/wear/wear-watchface-data/api/restricted_current.txt
@@ -182,22 +182,25 @@
     field public static final int ROUND_RECT = 0; // 0x0
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class ComplicationDetails implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
-    ctor public ComplicationDetails(android.graphics.Rect, @androidx.wear.watchface.data.ComplicationBoundsType int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean);
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class ComplicationStateWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
+    ctor public ComplicationStateWireFormat(android.graphics.Rect, @androidx.wear.watchface.data.ComplicationBoundsType int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean);
     method public int describeContents();
     method public android.graphics.Rect getBounds();
     method @androidx.wear.watchface.data.ComplicationBoundsType public int getBoundsType();
     method @android.support.wearable.complications.ComplicationData.ComplicationType public int getDefaultProviderType();
+    method public java.util.List<android.content.ComponentName!>? getDefaultProvidersToTry();
     method public int getFallbackSystemProvider();
     method @android.support.wearable.complications.ComplicationData.ComplicationType public int[] getSupportedTypes();
     method public boolean isEnabled();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<androidx.wear.watchface.data.ComplicationDetails!>! CREATOR;
+    field public static final android.os.Parcelable.Creator<androidx.wear.watchface.data.ComplicationStateWireFormat!>! CREATOR;
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class DeviceConfig implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
-    ctor public DeviceConfig(boolean, boolean, int);
+    ctor public DeviceConfig(boolean, boolean, int, long, long);
     method public int describeContents();
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
     method public int getScreenShape();
@@ -216,13 +219,13 @@
     field public static final android.os.Parcelable.Creator<androidx.wear.watchface.data.IdAndComplicationDataWireFormat!>! CREATOR;
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class IdAndComplicationDetails implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
-    ctor public IdAndComplicationDetails(int, androidx.wear.watchface.data.ComplicationDetails);
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class IdAndComplicationStateWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
+    ctor public IdAndComplicationStateWireFormat(int, androidx.wear.watchface.data.ComplicationStateWireFormat);
     method public int describeContents();
-    method public androidx.wear.watchface.data.ComplicationDetails getComplicationDetails();
+    method public androidx.wear.watchface.data.ComplicationStateWireFormat getComplicationState();
     method public int getId();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<androidx.wear.watchface.data.IdAndComplicationDetails!>! CREATOR;
+    field public static final android.os.Parcelable.Creator<androidx.wear.watchface.data.IdAndComplicationStateWireFormat!>! CREATOR;
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public class RenderParametersWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
diff --git a/wear/wear-watchface-data/src/main/aidl/android/support/wearable/watchface/IWatchFaceService.aidl b/wear/wear-watchface-data/src/main/aidl/android/support/wearable/watchface/IWatchFaceService.aidl
index 270a81f..d0ca805 100644
--- a/wear/wear-watchface-data/src/main/aidl/android/support/wearable/watchface/IWatchFaceService.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/android/support/wearable/watchface/IWatchFaceService.aidl
@@ -20,7 +20,6 @@
 import android.os.Bundle;
 import android.support.wearable.watchface.accessibility.ContentDescriptionLabel;
 import android.support.wearable.watchface.WatchFaceStyle;
-import androidx.wear.watchface.data.ComplicationDetails;
 import androidx.wear.watchface.style.data.UserStyleWireFormat;
 import androidx.wear.watchface.style.data.UserStyleSchemaWireFormat;
 
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IHeadlessWatchFace.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IHeadlessWatchFace.aidl
index 2ea61b1..2fd8652 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IHeadlessWatchFace.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IHeadlessWatchFace.aidl
@@ -18,7 +18,7 @@
 
 import androidx.wear.watchface.control.data.ComplicationScreenshotParams;
 import androidx.wear.watchface.control.data.WatchfaceScreenshotParams;
-import androidx.wear.watchface.data.IdAndComplicationDetails;
+import androidx.wear.watchface.data.IdAndComplicationStateWireFormat;
 import androidx.wear.watchface.style.data.UserStyleSchemaWireFormat;
 
 /**
@@ -60,12 +60,12 @@
     UserStyleSchemaWireFormat getUserStyleSchema() = 3;
 
     /**
-      * Returns the current {@link ComplicationDetails} for each complication slot. Note these
+      * Returns the current {@link ComplicationStateWireFormat} for each complication slot. Note these
       * details can change, typically in response to styling.
       *
       * @since API version 1.
       */
-    List<IdAndComplicationDetails> getComplicationDetails() = 4;
+    List<IdAndComplicationStateWireFormat> getComplicationState() = 4;
 
     /**
      * Request for a {@link Bundle} containing a WebP compressed shared memory backed {@link Bitmap}
@@ -99,4 +99,4 @@
      * @since API version 1.
      */
     oneway void release() = 7;
-}
\ No newline at end of file
+}
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceSysUI.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceSysUI.aidl
index 6560a52..dd86be4 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceSysUI.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceSysUI.aidl
@@ -131,4 +131,4 @@
      * @since API version 1.
      */
     oneway void release() = 9;
-}
\ No newline at end of file
+}
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceWCS.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceWCS.aidl
index 64d1ac0..40eb511 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceWCS.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFaceWCS.aidl
@@ -19,7 +19,7 @@
 import androidx.wear.watchface.control.data.WatchfaceScreenshotParams;
 import androidx.wear.watchface.data.SystemState;
 import androidx.wear.watchface.data.IdAndComplicationDataWireFormat;
-import androidx.wear.watchface.data.IdAndComplicationDetails;
+import androidx.wear.watchface.data.IdAndComplicationStateWireFormat;
 import androidx.wear.watchface.style.data.UserStyleSchemaWireFormat;
 import androidx.wear.watchface.style.data.UserStyleWireFormat;
 
@@ -85,13 +85,13 @@
     UserStyleSchemaWireFormat getUserStyleSchema() = 8;
 
     /**
-      * Returns the current {@link ComplicationDetails} for each complication slot. Note these
+      * Returns the current {@link ComplicationState} for each complication slot. Note these
       * details can change, typically in response to styling.
       *
       * @since API version 1.
-      * @param allComplicationSlots Map of id to {@link ComplicationDetails} for each slot.
+      * @param allComplicationSlots Map of id to {@link ComplicationState} for each slot.
       */
-    List<IdAndComplicationDetails> getComplicationDetails() = 9;
+    List<IdAndComplicationStateWireFormat> getComplicationDetails() = 9;
 
     /**
      * Request for a {@link Bundle} containing a WebP compressed shared memory backed {@link Bitmap}
@@ -114,4 +114,4 @@
      * @since API version 1.
      */
     oneway void release() = 11;
-}
\ No newline at end of file
+}
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationStateWireFormat.aidl
similarity index 94%
rename from wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
rename to wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationStateWireFormat.aidl
index 4b53020..188e7f8 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationStateWireFormat.aidl
@@ -17,4 +17,4 @@
 package androidx.wear.watchface.data;
 
 /** @hide */
-parcelable ComplicationDetails;
\ No newline at end of file
+parcelable ComplicationStateWireFormat;
\ No newline at end of file
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationDetails.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationDetails.aidl
deleted file mode 100644
index 0264bd1..0000000
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationDetails.aidl
+++ /dev/null
@@ -1,20 +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.wear.watchface.data;
-
-/** @hide */
-parcelable IdAndComplicationDetails;
\ No newline at end of file
diff --git a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.aidl
similarity index 93%
copy from wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
copy to wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.aidl
index 4b53020..4f8bb36 100644
--- a/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/ComplicationDetails.aidl
+++ b/wear/wear-watchface-data/src/main/aidl/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.aidl
@@ -17,4 +17,4 @@
 package androidx.wear.watchface.data;
 
 /** @hide */
-parcelable ComplicationDetails;
\ No newline at end of file
+parcelable IdAndComplicationStateWireFormat;
\ No newline at end of file
diff --git a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationDetails.java b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
similarity index 81%
rename from wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationDetails.java
rename to wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
index 58cfd9e..9d5b032 100644
--- a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationDetails.java
+++ b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
@@ -41,7 +41,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
 @VersionedParcelize
 @SuppressLint("BanParcelableUsage") // TODO(b/169214666): Remove Parcelable
-public final class ComplicationDetails implements VersionedParcelable, Parcelable {
+public final class ComplicationStateWireFormat implements VersionedParcelable, Parcelable {
     @ParcelField(1)
     @NonNull
     Rect mBounds;
@@ -70,10 +70,10 @@
     boolean mIsEnabled;
 
     /** Used by VersionedParcelable. */
-    ComplicationDetails() {
+    ComplicationStateWireFormat() {
     }
 
-    public ComplicationDetails(
+    public ComplicationStateWireFormat(
             @NonNull Rect bounds,
             @ComplicationBoundsType int boundsType,
             @NonNull @ComplicationData.ComplicationType int[] supportedTypes,
@@ -106,11 +106,19 @@
         return mSupportedTypes;
     }
 
+    /**
+     * Along with {@link #getFallbackSystemProvider} this is the wire format for
+     * DefaultComplicationProviderPolicy.
+     */
     @Nullable
-    List<ComponentName> getDefaultProvidersToTry() {
+    public List<ComponentName> getDefaultProvidersToTry() {
         return mDefaultProvidersToTry;
     }
 
+    /**
+     * Along with {@link #getDefaultProvidersToTry} this is the wire format for
+     * DefaultComplicationProviderPolicy.
+     */
     public int getFallbackSystemProvider() {
         return mFallbackSystemProvider;
     }
@@ -135,17 +143,17 @@
         return 0;
     }
 
-    public static final Parcelable.Creator<ComplicationDetails> CREATOR =
-            new Parcelable.Creator<ComplicationDetails>() {
+    public static final Parcelable.Creator<ComplicationStateWireFormat> CREATOR =
+            new Parcelable.Creator<ComplicationStateWireFormat>() {
                 @Override
-                public ComplicationDetails createFromParcel(Parcel source) {
+                public ComplicationStateWireFormat createFromParcel(Parcel source) {
                     return ParcelUtils.fromParcelable(
                             source.readParcelable(getClass().getClassLoader()));
                 }
 
                 @Override
-                public ComplicationDetails[] newArray(int size) {
-                    return new ComplicationDetails[size];
+                public ComplicationStateWireFormat[] newArray(int size) {
+                    return new ComplicationStateWireFormat[size];
                 }
             };
 }
diff --git a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/DeviceConfig.java b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/DeviceConfig.java
index 4fb5513..8efb65e 100644
--- a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/DeviceConfig.java
+++ b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/DeviceConfig.java
@@ -52,16 +52,26 @@
     @ParcelField(3)
     int mScreenShape;
 
+    @ParcelField(4)
+    long mAnalogPreviewReferenceTimeMillis;
+
+    @ParcelField(5)
+    long mDigitalPreviewReferenceTimeMillis;
+
     /** Used by VersionedParcelable. */
     DeviceConfig() {}
 
     public DeviceConfig(
             boolean hasLowBitAmbient,
             boolean hasBurnInProtection,
-            int screenShape) {
+            int screenShape,
+            long analogPreviewReferenceTimeMillis,
+            long digitalPreviewReferenceTimeMillis) {
         mHasLowBitAmbient = hasLowBitAmbient;
         mHasBurnInProtection = hasBurnInProtection;
         mScreenShape = screenShape;
+        mAnalogPreviewReferenceTimeMillis = analogPreviewReferenceTimeMillis;
+        mDigitalPreviewReferenceTimeMillis = digitalPreviewReferenceTimeMillis;
     }
 
     public boolean getHasLowBitAmbient() {
@@ -76,6 +86,14 @@
         return mScreenShape;
     }
 
+    public long getAnalogPreviewReferenceTimeMillis() {
+        return mAnalogPreviewReferenceTimeMillis;
+    }
+
+    public long getDigitalPreviewReferenceTimeMillis() {
+        return mDigitalPreviewReferenceTimeMillis;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationDetails.java b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.java
similarity index 63%
rename from wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationDetails.java
rename to wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.java
index e250807..ba9a096 100644
--- a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationDetails.java
+++ b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/IdAndComplicationStateWireFormat.java
@@ -28,30 +28,31 @@
 import androidx.versionedparcelable.VersionedParcelize;
 
 /**
- * Wire format to encode a pair of id to {@link ComplicationDetails}.
+ * Wire format to encode a pair of id to {@link ComplicationStateWireFormat}.
  *
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
 @VersionedParcelize
 @SuppressLint("BanParcelableUsage") // TODO(b/169214666): Remove Parcelable
-public final class IdAndComplicationDetails implements VersionedParcelable, Parcelable {
+public final class IdAndComplicationStateWireFormat implements VersionedParcelable, Parcelable {
     /** The watch's ID for the complication. */
     @ParcelField(1)
     int mId;
 
-    /** The {@link ComplicationDetails} for the complication. */
+    /** The {@link ComplicationStateWireFormat} for the complication. */
     @ParcelField(2)
     @NonNull
-    ComplicationDetails mComplicationDetails;
+    ComplicationStateWireFormat mComplicationState;
 
     /** Used by VersionedParcelable. */
-    IdAndComplicationDetails() {
+    IdAndComplicationStateWireFormat() {
     }
 
-    public IdAndComplicationDetails(int id, @NonNull ComplicationDetails complicationDetails) {
+    public IdAndComplicationStateWireFormat(
+            int id, @NonNull ComplicationStateWireFormat complicationState) {
         mId = id;
-        mComplicationDetails = complicationDetails;
+        mComplicationState = complicationState;
     }
 
     public int getId() {
@@ -59,11 +60,11 @@
     }
 
     @NonNull
-    public ComplicationDetails getComplicationDetails() {
-        return mComplicationDetails;
+    public ComplicationStateWireFormat getComplicationState() {
+        return mComplicationState;
     }
 
-    /** Serializes this IdAndComplicationDetails to the specified {@link Parcel}. */
+    /** Serializes this IdAndComplicationStateWireFormat to the specified {@link Parcel}. */
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeParcelable(ParcelUtils.toParcelable(this), flags);
@@ -74,17 +75,17 @@
         return 0;
     }
 
-    public static final Parcelable.Creator<IdAndComplicationDetails> CREATOR =
-            new Parcelable.Creator<IdAndComplicationDetails>() {
+    public static final Parcelable.Creator<IdAndComplicationStateWireFormat> CREATOR =
+            new Parcelable.Creator<IdAndComplicationStateWireFormat>() {
                 @Override
-                public IdAndComplicationDetails createFromParcel(Parcel source) {
+                public IdAndComplicationStateWireFormat createFromParcel(Parcel source) {
                     return ParcelUtils.fromParcelable(
                             source.readParcelable(getClass().getClassLoader()));
                 }
 
                 @Override
-                public IdAndComplicationDetails[] newArray(int size) {
-                    return new IdAndComplicationDetails[size];
+                public IdAndComplicationStateWireFormat[] newArray(int size) {
+                    return new IdAndComplicationStateWireFormat[size];
                 }
             };
 }
diff --git a/wear/wear-watchface-style/api/current.txt b/wear/wear-watchface-style/api/current.txt
index 66d782d..fbbc007 100644
--- a/wear/wear-watchface-style/api/current.txt
+++ b/wear/wear-watchface-style/api/current.txt
@@ -20,15 +20,15 @@
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
-    method public Integer? getDefaultProviderType();
-    method public int[]? getSupportedTypes();
+    method public androidx.wear.complications.data.ComplicationType? getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType>? getSupportedTypes();
     method public Boolean? isEnabled();
     property public final android.graphics.RectF? bounds;
     property public final int complicationId;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy;
-    property public final Integer? defaultProviderType;
+    property public final androidx.wear.complications.data.ComplicationType? defaultProviderType;
     property public final Boolean? enabled;
-    property public final int[]? supportedTypes;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType>? supportedTypes;
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationOverlay.Builder {
@@ -36,9 +36,9 @@
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay build();
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setBounds(android.graphics.RectF bounds);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy? defaultComplicationProviderPolicy);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(int defaultComplicationProviderType);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultComplicationProviderType);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setEnabled(boolean enabled);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(int[] supportedTypes);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes);
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationsOption extends androidx.wear.watchface.style.UserStyleSetting.Option {
diff --git a/wear/wear-watchface-style/api/public_plus_experimental_current.txt b/wear/wear-watchface-style/api/public_plus_experimental_current.txt
index ae7a0a0..263253a 100644
--- a/wear/wear-watchface-style/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-style/api/public_plus_experimental_current.txt
@@ -23,15 +23,15 @@
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
-    method public Integer? getDefaultProviderType();
-    method public int[]? getSupportedTypes();
+    method public androidx.wear.complications.data.ComplicationType? getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType>? getSupportedTypes();
     method public Boolean? isEnabled();
     property public final android.graphics.RectF? bounds;
     property public final int complicationId;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy;
-    property public final Integer? defaultProviderType;
+    property public final androidx.wear.complications.data.ComplicationType? defaultProviderType;
     property public final Boolean? enabled;
-    property public final int[]? supportedTypes;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType>? supportedTypes;
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationOverlay.Builder {
@@ -39,9 +39,9 @@
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay build();
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setBounds(android.graphics.RectF bounds);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy? defaultComplicationProviderPolicy);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(@android.support.wearable.complications.ComplicationData.ComplicationType int defaultComplicationProviderType);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultComplicationProviderType);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setEnabled(boolean enabled);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(int[] supportedTypes);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes);
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationsOption extends androidx.wear.watchface.style.UserStyleSetting.Option {
diff --git a/wear/wear-watchface-style/api/restricted_current.txt b/wear/wear-watchface-style/api/restricted_current.txt
index 0c8fd91..243f653 100644
--- a/wear/wear-watchface-style/api/restricted_current.txt
+++ b/wear/wear-watchface-style/api/restricted_current.txt
@@ -23,15 +23,15 @@
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
-    method public Integer? getDefaultProviderType();
-    method public int[]? getSupportedTypes();
+    method public androidx.wear.complications.data.ComplicationType? getDefaultProviderType();
+    method public java.util.List<androidx.wear.complications.data.ComplicationType>? getSupportedTypes();
     method public Boolean? isEnabled();
     property public final android.graphics.RectF? bounds;
     property public final int complicationId;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy;
-    property public final Integer? defaultProviderType;
+    property public final androidx.wear.complications.data.ComplicationType? defaultProviderType;
     property public final Boolean? enabled;
-    property public final int[]? supportedTypes;
+    property public final java.util.List<androidx.wear.complications.data.ComplicationType>? supportedTypes;
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationOverlay.Builder {
@@ -39,9 +39,9 @@
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay build();
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setBounds(android.graphics.RectF bounds);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy? defaultComplicationProviderPolicy);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(@android.support.wearable.complications.ComplicationData.ComplicationType int defaultComplicationProviderType);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultComplicationProviderType);
     method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setEnabled(boolean enabled);
-    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(int[] supportedTypes);
+    method public androidx.wear.watchface.style.ComplicationsUserStyleSetting.ComplicationOverlay.Builder setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes);
   }
 
   public static final class ComplicationsUserStyleSetting.ComplicationsOption extends androidx.wear.watchface.style.UserStyleSetting.Option {
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/ComplicationsUserStyleSetting.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/ComplicationsUserStyleSetting.kt
index f99b8ca..e40261e 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/ComplicationsUserStyleSetting.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/ComplicationsUserStyleSetting.kt
@@ -18,9 +18,9 @@
 
 import android.graphics.RectF
 import android.graphics.drawable.Icon
-import android.support.wearable.complications.ComplicationData
 import androidx.annotation.RestrictTo
 import androidx.wear.complications.DefaultComplicationProviderPolicy
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.style.data.ComplicationsUserStyleSettingWireFormat
 import androidx.wear.watchface.style.data.ComplicationsUserStyleSettingWireFormat.ComplicationOverlayWireFormat
 import androidx.wear.watchface.style.data.ComplicationsUserStyleSettingWireFormat.ComplicationsOptionWireFormat
@@ -66,7 +66,7 @@
          * If non null, the new types of complication supported by this complication for this
          * configuration. If null then no changes are made.
          */
-        public val supportedTypes: IntArray?,
+        public val supportedTypes: List<ComplicationType>?,
 
         /**
          * If non null, the new default complication provider for this configuration. If null then
@@ -75,11 +75,10 @@
         public val defaultProviderPolicy: DefaultComplicationProviderPolicy?,
 
         /**
-         * If non null, the new default complication provider data type. See
-         * [ComplicationData .ComplicationType].  If null then no changes are made.
+         * If non null, the new default complication provider data type. If null then no changes are
+         * made.
          */
-        @get:SuppressWarnings("AutoBoxing")
-        public val defaultProviderType: Int?
+        public val defaultProviderType: ComplicationType?
     ) {
         public class Builder(
             /** The id of the complication to configure. */
@@ -87,9 +86,9 @@
         ) {
             private var enabled: Boolean? = null
             private var bounds: RectF? = null
-            private var supportedTypes: IntArray? = null
+            private var supportedTypes: List<ComplicationType>? = null
             private var defaultComplicationProviderPolicy: DefaultComplicationProviderPolicy? = null
-            private var defaultComplicationProviderType: Int? = null
+            private var defaultComplicationProviderType: ComplicationType? = null
 
             /** Overrides the complication's enabled flag. */
             public fun setEnabled(enabled: Boolean): Builder = apply {
@@ -102,7 +101,7 @@
             }
 
             /** Overrides the complication's supported complication types. */
-            public fun setSupportedTypes(supportedTypes: IntArray): Builder = apply {
+            public fun setSupportedTypes(supportedTypes: List<ComplicationType>): Builder = apply {
                 this.supportedTypes = supportedTypes
             }
 
@@ -114,11 +113,10 @@
             }
 
             /**
-             * Overrides the default complication provider data type. See
-             * [ComplicationData.ComplicationType]
+             * Overrides the default complication provider data type.
              */
             public fun setDefaultProviderType(
-                @ComplicationData.ComplicationType defaultComplicationProviderType: Int
+                defaultComplicationProviderType: ComplicationType
             ): Builder = apply {
                 this.defaultComplicationProviderType = defaultComplicationProviderType
             }
@@ -146,14 +144,14 @@
                 )
             },
             wireFormat.mBounds,
-            wireFormat.mSupportedTypes,
+            wireFormat.mSupportedTypes?.let { ComplicationType.fromWireTypeList(it) },
             wireFormat.mDefaultProviders?.let {
                 DefaultComplicationProviderPolicy(it, wireFormat.mSystemProviderFallback)
             },
             if (wireFormat.mDefaultProviderType !=
                 ComplicationOverlayWireFormat.NO_DEFAULT_PROVIDER_TYPE
             ) {
-                wireFormat.mDefaultProviderType
+                ComplicationType.fromWireType(wireFormat.mDefaultProviderType)
             } else {
                 null
             }
@@ -164,10 +162,10 @@
                 complicationId,
                 enabled,
                 bounds,
-                supportedTypes,
+                supportedTypes?.let { ComplicationType.toWireTypes(it) },
                 defaultProviderPolicy?.providersAsList(),
                 defaultProviderPolicy?.systemProviderFallback,
-                defaultProviderType
+                defaultProviderType?.asWireComplicationType()
             )
     }
 
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index a868040..406bd24 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -37,32 +37,32 @@
 
   public final class Complication {
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
-    method @UiThread public int getDefaultProviderType();
+    method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
     method @UiThread public androidx.wear.watchface.CanvasComplication getRenderer();
-    method @UiThread public int[] getSupportedTypes();
+    method @UiThread public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
     method @UiThread public android.graphics.RectF getUnitSquareBounds();
     method public void invalidate();
     method @UiThread public boolean isEnabled();
     method @UiThread public void render(android.graphics.Canvas canvas, android.icu.util.Calendar calendar, androidx.wear.watchface.RenderParameters renderParameters);
     method @UiThread public void setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy value);
-    method @UiThread public void setDefaultProviderType(int value);
+    method @UiThread public void setDefaultProviderType(androidx.wear.complications.data.ComplicationType value);
     method @UiThread public void setEnabled(boolean value);
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
-    method @UiThread public void setSupportedTypes(int[] value);
+    method @UiThread public void setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> value);
     method @UiThread public void setUnitSquareBounds(android.graphics.RectF value);
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
-    property @UiThread public final int defaultProviderType;
+    property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property @UiThread public final boolean enabled;
     property @UiThread public final androidx.wear.watchface.CanvasComplication renderer;
-    property @UiThread public final int[] supportedTypes;
+    property @UiThread public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
     property @UiThread public final android.graphics.RectF unitSquareBounds;
   }
 
   public static final class Complication.Builder {
-    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, int[] supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
+    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
     method public androidx.wear.watchface.Complication build();
     method public androidx.wear.watchface.Complication.Builder setAsBackgroundComplication();
-    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(int defaultProviderType);
+    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setUnitSquareBounds(android.graphics.RectF unitSquareBounds);
   }
 
@@ -217,25 +217,23 @@
   }
 
   public final class WatchState {
-    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible;
     property public final int screenShape;
-    field public static final androidx.wear.watchface.WatchState.Companion Companion;
-    field public static final int SCREEN_SHAPE_RECTANGULAR = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-  }
-
-  public static final class WatchState.Companion {
   }
 
 }
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index e7409a5..406bd24 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -37,32 +37,32 @@
 
   public final class Complication {
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
-    method @UiThread public int getDefaultProviderType();
+    method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
     method @UiThread public androidx.wear.watchface.CanvasComplication getRenderer();
-    method @UiThread public int[] getSupportedTypes();
+    method @UiThread public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
     method @UiThread public android.graphics.RectF getUnitSquareBounds();
     method public void invalidate();
     method @UiThread public boolean isEnabled();
     method @UiThread public void render(android.graphics.Canvas canvas, android.icu.util.Calendar calendar, androidx.wear.watchface.RenderParameters renderParameters);
     method @UiThread public void setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy value);
-    method @UiThread public void setDefaultProviderType(int value);
+    method @UiThread public void setDefaultProviderType(androidx.wear.complications.data.ComplicationType value);
     method @UiThread public void setEnabled(boolean value);
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
-    method @UiThread public void setSupportedTypes(int[] value);
+    method @UiThread public void setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> value);
     method @UiThread public void setUnitSquareBounds(android.graphics.RectF value);
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
-    property @UiThread public final int defaultProviderType;
+    property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property @UiThread public final boolean enabled;
     property @UiThread public final androidx.wear.watchface.CanvasComplication renderer;
-    property @UiThread public final int[] supportedTypes;
+    property @UiThread public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
     property @UiThread public final android.graphics.RectF unitSquareBounds;
   }
 
   public static final class Complication.Builder {
-    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, int[] supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
+    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
     method public androidx.wear.watchface.Complication build();
     method public androidx.wear.watchface.Complication.Builder setAsBackgroundComplication();
-    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(@android.support.wearable.complications.ComplicationData.ComplicationType int defaultProviderType);
+    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setUnitSquareBounds(android.graphics.RectF unitSquareBounds);
   }
 
@@ -217,25 +217,23 @@
   }
 
   public final class WatchState {
-    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible;
     property public final int screenShape;
-    field public static final androidx.wear.watchface.WatchState.Companion Companion;
-    field public static final int SCREEN_SHAPE_RECTANGULAR = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-  }
-
-  public static final class WatchState.Companion {
   }
 
 }
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index b563ca0..de4a85e 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -37,32 +37,32 @@
 
   public final class Complication {
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
-    method @UiThread public int getDefaultProviderType();
+    method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
     method @UiThread public androidx.wear.watchface.CanvasComplication getRenderer();
-    method @UiThread public int[] getSupportedTypes();
+    method @UiThread public java.util.List<androidx.wear.complications.data.ComplicationType> getSupportedTypes();
     method @UiThread public android.graphics.RectF getUnitSquareBounds();
     method public void invalidate();
     method @UiThread public boolean isEnabled();
     method @UiThread public void render(android.graphics.Canvas canvas, android.icu.util.Calendar calendar, androidx.wear.watchface.RenderParameters renderParameters);
     method @UiThread public void setDefaultProviderPolicy(androidx.wear.complications.DefaultComplicationProviderPolicy value);
-    method @UiThread public void setDefaultProviderType(int value);
+    method @UiThread public void setDefaultProviderType(androidx.wear.complications.data.ComplicationType value);
     method @UiThread public void setEnabled(boolean value);
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
-    method @UiThread public void setSupportedTypes(int[] value);
+    method @UiThread public void setSupportedTypes(java.util.List<? extends androidx.wear.complications.data.ComplicationType> value);
     method @UiThread public void setUnitSquareBounds(android.graphics.RectF value);
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
-    property @UiThread public final int defaultProviderType;
+    property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property @UiThread public final boolean enabled;
     property @UiThread public final androidx.wear.watchface.CanvasComplication renderer;
-    property @UiThread public final int[] supportedTypes;
+    property @UiThread public final java.util.List<androidx.wear.complications.data.ComplicationType> supportedTypes;
     property @UiThread public final android.graphics.RectF unitSquareBounds;
   }
 
   public static final class Complication.Builder {
-    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, int[] supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
+    ctor public Complication.Builder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy);
     method public androidx.wear.watchface.Complication build();
     method public androidx.wear.watchface.Complication.Builder setAsBackgroundComplication();
-    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(@android.support.wearable.complications.ComplicationData.ComplicationType int defaultProviderType);
+    method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setUnitSquareBounds(android.graphics.RectF unitSquareBounds);
   }
 
@@ -138,6 +138,8 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class MutableWatchState {
     method public androidx.wear.watchface.WatchState asWatchState();
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> getInterruptionFilter();
@@ -145,10 +147,14 @@
     method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging();
     method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isVisible();
+    method public void setAnalogPreviewReferenceTimeMillis(long p);
+    method public void setDigitalPreviewReferenceTimeMillis(long p);
     method public void setHasBurnInProtection(boolean p);
     method public void setHasLowBitAmbient(boolean p);
     method public void setInterruptionFilter(androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> p);
     method public void setScreenShape(int p);
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> interruptionFilter;
@@ -258,25 +264,23 @@
   }
 
   public final class WatchState {
-    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape);
+    ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
+    method public long getAnalogPreviewReferenceTimeMillis();
+    method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
+    property public final long analogPreviewReferenceTimeMillis;
+    property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient;
     property public final androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible;
     property public final int screenShape;
-    field public static final androidx.wear.watchface.WatchState.Companion Companion;
-    field public static final int SCREEN_SHAPE_RECTANGULAR = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-  }
-
-  public static final class WatchState.Companion {
   }
 
 }
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
index c6d1404..f850c65 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
@@ -25,10 +25,10 @@
 import android.graphics.RectF
 import android.graphics.drawable.Icon
 import android.icu.util.Calendar
-import android.support.wearable.complications.ComplicationData
 import android.view.SurfaceHolder
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.CanvasRenderer
 import androidx.wear.watchface.CanvasType
 import androidx.wear.watchface.Complication
@@ -191,30 +191,30 @@
                 Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
                 ).setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build(),
                 Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.STEP_COUNT)
                 ).setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build()
             ),
             userStyleRepository
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 670db40..f58adba 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
@@ -23,12 +23,12 @@
 import android.opengl.GLU
 import android.opengl.GLUtils
 import android.opengl.Matrix
-import android.support.wearable.complications.ComplicationData
 import android.util.Log
 import android.view.Gravity
 import android.view.SurfaceHolder
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsManager
 import androidx.wear.watchface.DrawMode
@@ -104,16 +104,16 @@
                 Complication.Builder(
                     EXAMPLE_OPENGL_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
                 ).setUnitSquareBounds(RectF(0.2f, 0.7f, 0.4f, 0.9f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build()
             ),
             userStyleRepository
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 eb773072..1cc417b 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
@@ -22,11 +22,11 @@
 import android.graphics.Rect
 import android.graphics.RectF
 import android.icu.util.Calendar
-import android.support.wearable.complications.ComplicationData
 import android.view.SurfaceHolder
 import androidx.annotation.Sampled
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.complications.rendering.ComplicationDrawable
 import androidx.wear.watchface.CanvasRenderer
 import androidx.wear.watchface.CanvasType
@@ -111,16 +111,16 @@
                             ComplicationDrawable(this),
                             watchState
                         ),
-                        intArrayOf(
-                            ComplicationData.TYPE_RANGED_VALUE,
-                            ComplicationData.TYPE_LONG_TEXT,
-                            ComplicationData.TYPE_SHORT_TEXT,
-                            ComplicationData.TYPE_ICON,
-                            ComplicationData.TYPE_SMALL_IMAGE
+                        listOf(
+                            ComplicationType.RANGED_VALUE,
+                            ComplicationType.LONG_TEXT,
+                            ComplicationType.SHORT_TEXT,
+                            ComplicationType.MONOCHROMATIC_IMAGE,
+                            ComplicationType.SMALL_IMAGE
                         ),
                         DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
                     ).setUnitSquareBounds(RectF(0.15625f, 0.1875f, 0.84375f, 0.3125f))
-                        .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                        .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                         .build(),
                     Complication.Builder(
                         /*id */ 1,
@@ -128,16 +128,16 @@
                             ComplicationDrawable(this),
                             watchState
                         ),
-                        intArrayOf(
-                            ComplicationData.TYPE_RANGED_VALUE,
-                            ComplicationData.TYPE_LONG_TEXT,
-                            ComplicationData.TYPE_SHORT_TEXT,
-                            ComplicationData.TYPE_ICON,
-                            ComplicationData.TYPE_SMALL_IMAGE
+                        listOf(
+                            ComplicationType.RANGED_VALUE,
+                            ComplicationType.LONG_TEXT,
+                            ComplicationType.SHORT_TEXT,
+                            ComplicationType.MONOCHROMATIC_IMAGE,
+                            ComplicationType.SMALL_IMAGE
                         ),
                         DefaultComplicationProviderPolicy(SystemProviders.STEP_COUNT)
                     ).setUnitSquareBounds(RectF(0.1f, 0.5625f, 0.35f, 0.8125f))
-                        .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                        .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                         .build()
                 ),
                 userStyleRepository
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
index 18dc481..cba42c8 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
@@ -20,10 +20,10 @@
 import android.graphics.RectF
 import android.graphics.drawable.Icon
 import android.os.Handler
-import android.support.wearable.complications.ComplicationData
 import android.view.SurfaceHolder
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsManager
 import androidx.wear.watchface.MutableWatchState
@@ -183,30 +183,30 @@
                 Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
                 ).setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build(),
                 Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.STEP_COUNT)
                 ).setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build()
             ),
             userStyleRepository
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
index e43b6ee..aa47c20 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
@@ -20,10 +20,10 @@
 import android.graphics.RectF
 import android.graphics.drawable.Icon
 import android.os.Handler
-import android.support.wearable.complications.ComplicationData
 import android.view.SurfaceHolder
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsManager
 import androidx.wear.watchface.MutableWatchState
@@ -91,16 +91,16 @@
                 Complication.Builder(
                     EXAMPLE_OPENGL_COMPLICATION_ID,
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
-                    intArrayOf(
-                        ComplicationData.TYPE_RANGED_VALUE,
-                        ComplicationData.TYPE_LONG_TEXT,
-                        ComplicationData.TYPE_SHORT_TEXT,
-                        ComplicationData.TYPE_ICON,
-                        ComplicationData.TYPE_SMALL_IMAGE
+                    listOf(
+                        ComplicationType.RANGED_VALUE,
+                        ComplicationType.LONG_TEXT,
+                        ComplicationType.SHORT_TEXT,
+                        ComplicationType.MONOCHROMATIC_IMAGE,
+                        ComplicationType.SMALL_IMAGE
                     ),
                     DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
                 ).setUnitSquareBounds(RectF(0.2f, 0.7f, 0.4f, 0.9f))
-                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .setDefaultProviderType(ComplicationType.SHORT_TEXT)
                     .build()
             ),
             userStyleRepository
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
index 17f55d8..7f81820 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
@@ -70,7 +70,9 @@
                 DeviceConfig(
                     false,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_ROUND
+                    DeviceConfig.SCREEN_SHAPE_ROUND,
+                    0,
+                    0
                 ),
                 width,
                 height
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index 8be6078..77491fc 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -139,7 +139,9 @@
                 DeviceConfig(
                     false,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_ROUND
+                    DeviceConfig.SCREEN_SHAPE_ROUND,
+                    0,
+                    0
                 ),
                 SystemState(false, 0),
                 null,
@@ -169,7 +171,9 @@
                 DeviceConfig(
                     false,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_ROUND
+                    DeviceConfig.SCREEN_SHAPE_ROUND,
+                    0,
+                    0
                 ),
                 SystemState(false, 0),
                 null,
@@ -210,7 +214,7 @@
             interactiveWatchFaceInstanceWCS.complicationDetails.map {
                 IdAndComplicationDataWireFormat(
                     it.id,
-                    complicationProviders[it.complicationDetails.fallbackSystemProvider]!!
+                    complicationProviders[it.complicationState.fallbackSystemProvider]!!
                 )
             }
         )
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 b7989b6..59b6ac5 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
@@ -25,6 +25,7 @@
 import android.support.wearable.complications.ComplicationData
 import androidx.annotation.UiThread
 import androidx.wear.complications.DefaultComplicationProviderPolicy
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.complications.data.IdAndComplicationData
 import androidx.wear.watchface.complications.rendering.ComplicationDrawable
 import androidx.wear.watchface.data.ComplicationBoundsType
@@ -204,9 +205,9 @@
     @ComplicationBoundsType internal val boundsType: Int,
     unitSquareBounds: RectF,
     canvasComplication: CanvasComplication,
-    supportedTypes: IntArray,
+    supportedTypes: List<ComplicationType>,
     defaultProviderPolicy: DefaultComplicationProviderPolicy,
-    defaultProviderType: Int
+    defaultProviderType: ComplicationType
 ) {
     /** @hide */
     private companion object {
@@ -232,7 +233,7 @@
          * [ComplicationHelperActivity.createProviderChooserHelperIntent] during complication
          * configuration.
          */
-        private val supportedTypes: IntArray,
+        private val supportedTypes: List<ComplicationType>,
 
         /** The [DefaultComplicationProviderPolicy] to use. */
         private val defaultProviderPolicy: DefaultComplicationProviderPolicy
@@ -241,13 +242,13 @@
         private var boundsType: Int? = null
         private lateinit var unitSquareBounds: RectF
 
-        private var defaultProviderType: Int = WatchFace.DEFAULT_PROVIDER_TYPE_NONE
+        private var defaultProviderType = ComplicationType.NOT_CONFIGURED
 
         /**
-         * Sets the default complication provider data type. See [ComplicationData.ComplicationType]
+         * Sets the default complication provider data type.
          */
         public fun setDefaultProviderType(
-            @ComplicationData.ComplicationType defaultProviderType: Int
+            defaultProviderType: ComplicationType
         ): Builder {
             this.defaultProviderType = defaultProviderType
             return this
@@ -385,7 +386,7 @@
     /**
      * The types of complications the complication supports.
      */
-    public var supportedTypes: IntArray
+    public var supportedTypes: List<ComplicationType>
         @UiThread
         get() = _supportedTypes
         @UiThread
@@ -428,21 +429,20 @@
             }
         }
 
-    private var _defaultProviderType = defaultProviderType
     internal var defaultProviderTypeDirty = true
 
     /**
      * The default [ComplicationData.ComplicationType] to use alongside [.defaultProviderPolicy].
      */
-    public var defaultProviderType: Int
+    public var defaultProviderType: ComplicationType = defaultProviderType
         @UiThread
-        get() = _defaultProviderType
+        get() = field
         @UiThread
         set(value) {
-            if (_defaultProviderType == value) {
+            if (field == value) {
                 return
             }
-            _defaultProviderType = value
+            field = value
             defaultProviderTypeDirty = true
 
             // The caller might modify a number of complications. For efficiency we need to
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 7cd19c3..19bb54e 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
@@ -89,9 +89,9 @@
         val id: Int,
         val unitSquareBounds: RectF,
         val enabled: Boolean,
-        val supportedTypes: IntArray,
+        val supportedTypes: List<ComplicationType>,
         val defaultProviderPolicy: DefaultComplicationProviderPolicy,
-        val defaultProviderType: Int
+        val defaultProviderType: ComplicationType
     )
 
     // Copy of the original complication configs. This is necessary because the semantics of
@@ -255,7 +255,7 @@
                         complication.id,
                         complication.defaultProviderPolicy.providersAsList(),
                         complication.defaultProviderPolicy.systemProviderFallback,
-                        complication.defaultProviderType
+                        complication.defaultProviderType.asWireComplicationType()
                     )
                 }
 
@@ -397,7 +397,9 @@
                 watchFaceHostApi.getContext(),
                 getComponentName(watchFaceHostApi.getContext()),
                 complicationId,
-                complication.supportedTypes
+                IntArray(complication.supportedTypes.size) {
+                    complication.supportedTypes[it].asWireComplicationType()
+                }
             ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
         )
         for (complicationListener in complicationListeners) {
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 eb6019e..e49580f 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
@@ -106,7 +106,8 @@
  */
 @SuppressLint("SyntheticAccessor")
 public class WatchFace private constructor(
-    internal val previewReferenceTimeMillis: Long,
+    @WatchFaceType watchFaceType: Int,
+    overridePreviewReferenceTimeMillis: Long?,
     private var interactiveUpdateRateMillis: Long,
     internal val userStyleRepository: UserStyleRepository,
     internal var complicationsManager: ComplicationsManager,
@@ -139,7 +140,7 @@
          * The type of watch face, whether it's digital or analog. Used to determine the
          * default time for editor preview screenshots.
          */
-        @WatchFaceType watchFaceType: Int,
+        @WatchFaceType private var watchFaceType: Int,
 
         /**
          * The interval in milliseconds between frames in interactive mode. To render at 60hz pass in
@@ -168,12 +169,7 @@
     ) {
         private var viewProtectionMode: Int = 0
         private var statusBarGravity: Int = 0
-        private var previewReferenceTimeMillis: Long =
-            when (watchFaceType) {
-                WatchFaceType.ANALOG -> ANALOG_WATCHFACE_REFERENCE_TIME_MS
-                WatchFaceType.DIGITAL -> DIGITAL_WATCHFACE_REFERENCE_TIME_MS
-                else -> throw InvalidParameterException("Unrecognized watchFaceType")
-            }
+        private var overridePreviewReferenceTimeMillis: Long? = null
 
         @ColorInt
         private var accentColor: Int = WatchFaceStyle.DEFAULT_ACCENT_COLOR
@@ -190,7 +186,7 @@
         public fun setPreviewReferenceTimeMillis(
             previewReferenceTimeMillis: Long
         ): Builder = apply {
-            this.previewReferenceTimeMillis = previewReferenceTimeMillis
+            overridePreviewReferenceTimeMillis = previewReferenceTimeMillis
         }
 
         /**
@@ -274,7 +270,8 @@
                     watchFaceHost.api!!.getContext().javaClass.typeName
                 )
             return WatchFace(
-                previewReferenceTimeMillis,
+                watchFaceType,
+                overridePreviewReferenceTimeMillis,
                 interactiveUpdateRateMillis,
                 userStyleRepository,
                 complicationsManager,
@@ -297,16 +294,7 @@
     }
 
     internal companion object {
-        // Reference time for editor screenshots for analog watch faces.
-        // 2020/10/10 at 09:30 Note the date doesn't matter, only the hour.
-        internal const val ANALOG_WATCHFACE_REFERENCE_TIME_MS = 1602318600000L
-
-        // Reference time for editor screenshots for digital watch faces.
-        // 2020/10/10 at 10:10 Note the date doesn't matter, only the hour.
-        internal const val DIGITAL_WATCHFACE_REFERENCE_TIME_MS = 1602321000000L
-
         internal const val NO_DEFAULT_PROVIDER = SystemProviders.NO_PROVIDER
-        internal const val DEFAULT_PROVIDER_TYPE_NONE = -2
 
         internal const val MOCK_TIME_INTENT = "androidx.wear.watchface.MockTime"
 
@@ -413,6 +401,13 @@
         }
     }
 
+    internal val previewReferenceTimeMillis =
+        overridePreviewReferenceTimeMillis ?: when (watchFaceType) {
+            WatchFaceType.ANALOG -> watchState.analogPreviewReferenceTimeMillis
+            WatchFaceType.DIGITAL -> watchState.digitalPreviewReferenceTimeMillis
+            else -> throw InvalidParameterException("Unrecognized watchFaceType")
+        }
+
     init {
         // If the system has a stored user style then Home/SysUI is in charge of style
         // persistence, otherwise we need to do our own.
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 f11bf97..2f03b09 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
@@ -47,6 +47,7 @@
 import androidx.annotation.UiThread
 import androidx.wear.complications.SystemProviders.ProviderId
 import androidx.wear.complications.data.ComplicationData
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.complications.data.IdAndComplicationData
 import androidx.wear.complications.data.NoDataComplicationData
 import androidx.wear.complications.data.asApiComplicationData
@@ -61,11 +62,11 @@
 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.ComplicationDetails
+import androidx.wear.watchface.data.ComplicationStateWireFormat
 import androidx.wear.watchface.data.DeviceConfig
 import androidx.wear.watchface.data.DeviceConfig.SCREEN_SHAPE_ROUND
 import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
-import androidx.wear.watchface.data.IdAndComplicationDetails
+import androidx.wear.watchface.data.IdAndComplicationStateWireFormat
 import androidx.wear.watchface.data.SystemState
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.data.UserStyleWireFormat
@@ -209,6 +210,14 @@
 
         /** Whether to enable tracing for each call to [Engine.onDraw]. */
         private const val TRACE_DRAW = false
+
+        // Reference time for editor screenshots for analog watch faces.
+        // 2020/10/10 at 09:30 Note the date doesn't matter, only the hour.
+        private const val ANALOG_WATCHFACE_REFERENCE_TIME_MS = 1602318600000L
+
+        // Reference time for editor screenshots for digital watch faces.
+        // 2020/10/10 at 10:10 Note the date doesn't matter, only the hour.
+        private const val DIGITAL_WATCHFACE_REFERENCE_TIME_MS = 1602321000000L
     }
 
     /** Override this factory method to create your WatchFace. */
@@ -360,6 +369,10 @@
                 mutableWatchState.hasBurnInProtection =
                     deviceConfig.hasBurnInProtection
                 mutableWatchState.screenShape = deviceConfig.screenShape
+                mutableWatchState.analogPreviewReferenceTimeMillis =
+                    deviceConfig.analogPreviewReferenceTimeMillis
+                mutableWatchState.digitalPreviewReferenceTimeMillis =
+                    deviceConfig.digitalPreviewReferenceTimeMillis
 
                 immutableSystemStateDone = true
             }
@@ -377,18 +390,18 @@
         }
 
         @UiThread
-        fun getComplicationDetails(): List<IdAndComplicationDetails> =
+        fun getComplicationState(): List<IdAndComplicationStateWireFormat> =
             uiThreadHandler.runOnHandler {
                 watchFace.complicationsManager.complications.map {
-                    IdAndComplicationDetails(
+                    IdAndComplicationStateWireFormat(
                         it.key,
-                        ComplicationDetails(
+                        ComplicationStateWireFormat(
                             it.value.computeBounds(watchFace.renderer.screenBounds),
                             it.value.boundsType,
-                            it.value.supportedTypes,
+                            ComplicationType.toWireTypes(it.value.supportedTypes),
                             it.value.defaultProviderPolicy.providersAsList(),
                             it.value.defaultProviderPolicy.systemProviderFallback,
-                            it.value.defaultProviderType,
+                            it.value.defaultProviderType.asWireComplicationType(),
                             it.value.enabled
                         )
                     )
@@ -976,7 +989,9 @@
                 DeviceConfig(
                     properties.getBoolean(Constants.PROPERTY_LOW_BIT_AMBIENT),
                     properties.getBoolean(Constants.PROPERTY_BURN_IN_PROTECTION),
-                    SCREEN_SHAPE_ROUND // TODO(alexclarke): Fix this?
+                    SCREEN_SHAPE_ROUND, // TODO(alexclarke): Fix this?
+                    ANALOG_WATCHFACE_REFERENCE_TIME_MS,
+                    DIGITAL_WATCHFACE_REFERENCE_TIME_MS
                 )
             )
         }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index 669396a..4f18d4d 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -20,6 +20,25 @@
 import androidx.annotation.RestrictTo
 import androidx.wear.watchface.data.DeviceConfig
 
+import androidx.annotation.IntDef
+
+/** @hide */
+@IntDef(
+    value = [
+        ScreenShape.ROUND,
+        ScreenShape.RECTANGULAR
+    ]
+)
+public annotation class ScreenShape {
+    public companion object {
+        /** The watch screen has a circular shape. */
+        public const val ROUND: Int = DeviceConfig.SCREEN_SHAPE_ROUND
+
+        /** The watch screen has a rectangular or square shape. */
+        public const val RECTANGULAR: Int = DeviceConfig.SCREEN_SHAPE_RECTANGULAR
+    }
+}
+
 public class WatchState(
     /**
      * The current user interruption settings. See [NotificationManager]. Based on the value
@@ -59,21 +78,16 @@
     /** Whether or not the watch hardware supports burn in protection. */
     public val hasBurnInProtection: Boolean,
 
-    /**
-     * The physical shape of the screen. Should be one of [#SCREEN_SHAPE_ROUND] or
-     * [#SCREEN_SHAPE_RECTANGULAR].
-     */
-    public val screenShape: Int
-) {
-    public companion object {
-        /** The watch has a round (circular) screen. */
-        public const val SCREEN_SHAPE_ROUND: Int = DeviceConfig.SCREEN_SHAPE_ROUND
+    /** The physical shape of the screen. */
+    @ScreenShape
+    public val screenShape: Int,
 
-        /** The watch has a rectangular or square screen. */
-        public const val SCREEN_SHAPE_RECTANGULAR: Int =
-            DeviceConfig.SCREEN_SHAPE_RECTANGULAR
-    }
-}
+    /** UTC reference time for previews of analog watch faces in milliseconds since the epoch. */
+    public val analogPreviewReferenceTimeMillis: Long,
+
+    /** UTC reference time for previews of digital watch faces in milliseconds since the epoch. */
+    public val digitalPreviewReferenceTimeMillis: Long
+)
 
 /** @hide */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -85,7 +99,10 @@
     public val isVisible: MutableObservableWatchData<Boolean> = MutableObservableWatchData()
     public var hasLowBitAmbient: Boolean = false
     public var hasBurnInProtection: Boolean = false
+    @ScreenShape
     public var screenShape: Int = 0
+    public var analogPreviewReferenceTimeMillis: Long = 0
+    public var digitalPreviewReferenceTimeMillis: Long = 0
 
     public fun asWatchState(): WatchState = WatchState(
         interruptionFilter = interruptionFilter,
@@ -94,6 +111,8 @@
         isVisible = isVisible,
         hasLowBitAmbient = hasLowBitAmbient,
         hasBurnInProtection = hasBurnInProtection,
-        screenShape = screenShape
+        screenShape = screenShape,
+        analogPreviewReferenceTimeMillis = analogPreviewReferenceTimeMillis,
+        digitalPreviewReferenceTimeMillis = digitalPreviewReferenceTimeMillis
     )
 }
\ No newline at end of file
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
index ea9c699..d9dee19 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
@@ -38,8 +38,8 @@
 
     override fun getPreviewReferenceTimeMillis() = engine!!.watchFace.previewReferenceTimeMillis
 
-    override fun getComplicationDetails() =
-        uiThreadHandler.runOnHandler { engine!!.getComplicationDetails() }
+    override fun getComplicationState() =
+        uiThreadHandler.runOnHandler { engine!!.getComplicationState() }
 
     override fun takeComplicationScreenshot(params: ComplicationScreenshotParams) =
         uiThreadHandler.runOnHandler { engine!!.takeComplicationScreenshot(params) }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
index 13b2887..397e898 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
@@ -95,7 +95,7 @@
         }
 
         override fun getComplicationDetails() =
-            uiThreadHandler.runOnHandler { engine.getComplicationDetails() }
+            uiThreadHandler.runOnHandler { engine.getComplicationState() }
 
         override fun getUserStyleSchema() =
             uiThreadHandler.runOnHandler {
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ComplicationConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ComplicationConfigFragment.kt
index be64fe8..b8b0dec 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ComplicationConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ComplicationConfigFragment.kt
@@ -32,6 +32,7 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.fragment.app.Fragment
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.DrawMode
 import androidx.wear.watchface.LayerMode
 import androidx.wear.watchface.RenderParameters
@@ -156,7 +157,7 @@
         iWatchFaceConfig.brieflyHighlightComplicationId(complicationId)
         watchFaceConfigActivity.fragmentController.showComplicationConfig(
             complicationId,
-            *complication.supportedTypes
+            *ComplicationType.toWireTypes(complication.supportedTypes)
         )
         return true
     }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
index d443748..2858ccb 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
@@ -37,6 +37,7 @@
 import androidx.fragment.app.Fragment
 import androidx.recyclerview.widget.RecyclerView
 import androidx.wear.complications.ProviderInfoRetriever
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.R
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.widget.SwipeDismissFrameLayout
@@ -192,7 +193,7 @@
                     ]!!
                 watchFaceConfigActivity.fragmentController.showComplicationConfig(
                     backgroundComplication.id,
-                    *backgroundComplication.supportedTypes
+                    *ComplicationType.toWireTypes(backgroundComplication.supportedTypes)
                 )
             }
 
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
index b7c98c2b..ab74dce 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
@@ -32,6 +32,7 @@
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
 import androidx.wear.complications.ComplicationHelperActivity
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.UserStyleSchema
 
@@ -241,7 +242,7 @@
                 val >
                 fragmentController.showComplicationConfig(
                     onlyComplication.id,
-                    *onlyComplication.supportedTypes
+                    *ComplicationType.toWireTypes(onlyComplication.supportedTypes)
                 )
             }
 
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 285db12..4260ddb 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
@@ -35,6 +35,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.complications.rendering.ComplicationDrawable
 import androidx.wear.watchface.control.IInteractiveWatchFaceWCS
 import androidx.wear.watchface.control.IWallpaperWatchFaceControlService
@@ -155,15 +156,15 @@
             ).apply {
                 idAndData = createIdAndComplicationData(LEFT_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_RANGED_VALUE,
-                ComplicationData.TYPE_LONG_TEXT,
-                ComplicationData.TYPE_SHORT_TEXT,
-                ComplicationData.TYPE_ICON,
-                ComplicationData.TYPE_SMALL_IMAGE
+            listOf(
+                ComplicationType.RANGED_VALUE,
+                ComplicationType.LONG_TEXT,
+                ComplicationType.SHORT_TEXT,
+                ComplicationType.MONOCHROMATIC_IMAGE,
+                ComplicationType.SMALL_IMAGE
             ),
             DefaultComplicationProviderPolicy(SystemProviders.SUNRISE_SUNSET)
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
             .build()
 
@@ -176,15 +177,15 @@
             ).apply {
                 idAndData = createIdAndComplicationData(RIGHT_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_RANGED_VALUE,
-                ComplicationData.TYPE_LONG_TEXT,
-                ComplicationData.TYPE_SHORT_TEXT,
-                ComplicationData.TYPE_ICON,
-                ComplicationData.TYPE_SMALL_IMAGE
+            listOf(
+                ComplicationType.RANGED_VALUE,
+                ComplicationType.LONG_TEXT,
+                ComplicationType.SHORT_TEXT,
+                ComplicationType.MONOCHROMATIC_IMAGE,
+                ComplicationType.SMALL_IMAGE
             ),
             DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
             .build()
 
@@ -197,11 +198,11 @@
             ).apply {
                 idAndData = createIdAndComplicationData(BACKGROUND_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_LARGE_IMAGE
+            listOf(
+                ComplicationType.BACKGROUND_IMAGE
             ),
             DefaultComplicationProviderPolicy()
-        ).setDefaultProviderType(ComplicationData.TYPE_LARGE_IMAGE)
+        ).setDefaultProviderType(ComplicationType.BACKGROUND_IMAGE)
             .setAsBackgroundComplication()
             .build()
 
@@ -352,6 +353,7 @@
                 interactiveWatchFaceInstanceWCS = service.createInteractiveWatchFaceInstance(
                     wallpaperInteractiveWatchFaceInstanceParams
                 )
+                watchFace = testWatchFaceService.watchFace
             }
         }
 
@@ -1016,7 +1018,9 @@
                 DeviceConfig(
                     false,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_ROUND
+                    DeviceConfig.SCREEN_SHAPE_ROUND,
+                    0,
+                    0
                 ),
                 SystemState(false, 0),
                 UserStyle(
@@ -1051,7 +1055,9 @@
                 DeviceConfig(
                     false,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_ROUND
+                    DeviceConfig.SCREEN_SHAPE_ROUND,
+                    0,
+                    0
                 ),
                 SystemState(false, 0),
                 UserStyle(hashMapOf(watchHandStyleSetting to badStyleOption)).toWireFormat(),
@@ -1104,7 +1110,9 @@
                 DeviceConfig(
                     true,
                     false,
-                    DeviceConfig.SCREEN_SHAPE_RECTANGULAR
+                    DeviceConfig.SCREEN_SHAPE_RECTANGULAR,
+                    0,
+                    0
                 ),
                 SystemState(false, 0),
                 UserStyle(hashMapOf(watchHandStyleSetting to badStyleOption)).toWireFormat(),
@@ -1208,20 +1216,20 @@
         leftComplication.unitSquareBounds = RectF(0.3f, 0.3f, 0.5f, 0.5f)
         rightComplication.unitSquareBounds = RectF(0.7f, 0.75f, 0.9f, 0.95f)
 
-        val complicationDetails = engineWrapper.getComplicationDetails()
+        val complicationDetails = engineWrapper.getComplicationState()
         assertThat(complicationDetails[0].id).isEqualTo(LEFT_COMPLICATION_ID)
-        assertThat(complicationDetails[0].complicationDetails.boundsType).isEqualTo(
+        assertThat(complicationDetails[0].complicationState.boundsType).isEqualTo(
             ComplicationBoundsType.ROUND_RECT
         )
-        assertThat(complicationDetails[0].complicationDetails.bounds).isEqualTo(
+        assertThat(complicationDetails[0].complicationState.bounds).isEqualTo(
             Rect(30, 30, 50, 50)
         )
 
         assertThat(complicationDetails[1].id).isEqualTo(RIGHT_COMPLICATION_ID)
-        assertThat(complicationDetails[1].complicationDetails.boundsType).isEqualTo(
+        assertThat(complicationDetails[1].complicationState.boundsType).isEqualTo(
             ComplicationBoundsType.ROUND_RECT
         )
-        assertThat(complicationDetails[1].complicationDetails.bounds).isEqualTo(
+        assertThat(complicationDetails[1].complicationState.bounds).isEqualTo(
             Rect(70, 75, 90, 95)
         )
 
@@ -1336,13 +1344,13 @@
         val complication = Complication.Builder(
             LEFT_COMPLICATION_ID,
             CanvasComplicationDrawable(complicationDrawableLeft, watchState.asWatchState()),
-            intArrayOf(),
+            emptyList(),
             DefaultComplicationProviderPolicy(
                 provider1,
                 provider2,
                 SystemProviders.SUNRISE_SUNSET
             )
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
             .build()
         initEngine(WatchFaceType.ANALOG, listOf(complication), UserStyleSchema(emptyList()))
@@ -1364,13 +1372,13 @@
         val complication = Complication.Builder(
             LEFT_COMPLICATION_ID,
             CanvasComplicationDrawable(complicationDrawableLeft, watchState.asWatchState()),
-            intArrayOf(),
+            emptyList(),
             DefaultComplicationProviderPolicy(
                 provider1,
                 provider2,
                 SystemProviders.SUNRISE_SUNSET
             )
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
             .build()
         initEngine(
@@ -1395,16 +1403,64 @@
 
     @Test
     fun previewReferenceTimeMillisAnalog() {
-        initEngine(WatchFaceType.ANALOG, emptyList(), UserStyleSchema(emptyList()), apiVersion = 4)
-        assertThat(watchFace.previewReferenceTimeMillis)
-            .isEqualTo(WatchFace.ANALOG_WATCHFACE_REFERENCE_TIME_MS)
+        val instanceParams = WallpaperInteractiveWatchFaceInstanceParams(
+            "interactiveInstanceId",
+            DeviceConfig(
+                false,
+                false,
+                DeviceConfig.SCREEN_SHAPE_ROUND,
+                1000,
+                2000,
+            ),
+            SystemState(false, 0),
+            UserStyle(
+                hashMapOf(
+                    colorStyleSetting to blueStyleOption,
+                    watchHandStyleSetting to gothicStyleOption
+                )
+            ).toWireFormat(),
+            null
+        )
+
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            emptyList(),
+            UserStyleSchema(listOf(colorStyleSetting, watchHandStyleSetting)),
+            instanceParams
+        )
+
+        assertThat(watchFace.previewReferenceTimeMillis).isEqualTo(1000)
     }
 
     @Test
     fun previewReferenceTimeMillisDigital() {
-        initEngine(WatchFaceType.DIGITAL, emptyList(), UserStyleSchema(emptyList()), apiVersion = 4)
-        assertThat(watchFace.previewReferenceTimeMillis)
-            .isEqualTo(WatchFace.DIGITAL_WATCHFACE_REFERENCE_TIME_MS)
+        val instanceParams = WallpaperInteractiveWatchFaceInstanceParams(
+            "interactiveInstanceId",
+            DeviceConfig(
+                false,
+                false,
+                DeviceConfig.SCREEN_SHAPE_ROUND,
+                1000,
+                2000,
+            ),
+            SystemState(false, 0),
+            UserStyle(
+                hashMapOf(
+                    colorStyleSetting to blueStyleOption,
+                    watchHandStyleSetting to gothicStyleOption
+                )
+            ).toWireFormat(),
+            null
+        )
+
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.DIGITAL,
+            emptyList(),
+            UserStyleSchema(listOf(colorStyleSetting, watchHandStyleSetting)),
+            instanceParams
+        )
+
+        assertThat(watchFace.previewReferenceTimeMillis).isEqualTo(2000)
     }
 
     @Test
@@ -1416,15 +1472,15 @@
             apiVersion = 4
         )
 
-        val complicationDetails = engineWrapper.getComplicationDetails()
+        val complicationDetails = engineWrapper.getComplicationState()
         assertThat(complicationDetails[0].id).isEqualTo(LEFT_COMPLICATION_ID)
-        assertThat(complicationDetails[0].complicationDetails.boundsType).isEqualTo(
+        assertThat(complicationDetails[0].complicationState.boundsType).isEqualTo(
             ComplicationBoundsType.ROUND_RECT
         )
-        assertThat(complicationDetails[0].complicationDetails.bounds).isEqualTo(
+        assertThat(complicationDetails[0].complicationState.bounds).isEqualTo(
             Rect(20, 40, 40, 60)
         )
-        assertThat(complicationDetails[0].complicationDetails.supportedTypes).isEqualTo(
+        assertThat(complicationDetails[0].complicationState.supportedTypes).isEqualTo(
             intArrayOf(
                 ComplicationData.TYPE_RANGED_VALUE,
                 ComplicationData.TYPE_LONG_TEXT,
@@ -1435,13 +1491,13 @@
         )
 
         assertThat(complicationDetails[1].id).isEqualTo(RIGHT_COMPLICATION_ID)
-        assertThat(complicationDetails[1].complicationDetails.boundsType).isEqualTo(
+        assertThat(complicationDetails[1].complicationState.boundsType).isEqualTo(
             ComplicationBoundsType.ROUND_RECT
         )
-        assertThat(complicationDetails[1].complicationDetails.bounds).isEqualTo(
+        assertThat(complicationDetails[1].complicationState.bounds).isEqualTo(
             Rect(60, 40, 80, 60)
         )
-        assertThat(complicationDetails[1].complicationDetails.supportedTypes).isEqualTo(
+        assertThat(complicationDetails[1].complicationState.supportedTypes).isEqualTo(
             intArrayOf(
                 ComplicationData.TYPE_RANGED_VALUE,
                 ComplicationData.TYPE_LONG_TEXT,
@@ -1452,13 +1508,13 @@
         )
 
         assertThat(complicationDetails[2].id).isEqualTo(BACKGROUND_COMPLICATION_ID)
-        assertThat(complicationDetails[2].complicationDetails.boundsType).isEqualTo(
+        assertThat(complicationDetails[2].complicationState.boundsType).isEqualTo(
             ComplicationBoundsType.BACKGROUND
         )
-        assertThat(complicationDetails[2].complicationDetails.bounds).isEqualTo(
+        assertThat(complicationDetails[2].complicationState.bounds).isEqualTo(
             Rect(0, 0, 100, 100)
         )
-        assertThat(complicationDetails[2].complicationDetails.supportedTypes).isEqualTo(
+        assertThat(complicationDetails[2].complicationState.supportedTypes).isEqualTo(
             intArrayOf(ComplicationData.TYPE_LARGE_IMAGE)
         )
     }
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
index 6f42710..38c6d6f6 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
@@ -22,11 +22,11 @@
 import android.graphics.Rect
 import android.graphics.RectF
 import android.icu.util.Calendar
-import android.support.wearable.complications.ComplicationData
 import android.view.SurfaceHolder
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.SystemProviders
+import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.CanvasComplicationDrawable
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsManager
@@ -126,15 +126,15 @@
             ).apply {
                 idAndData = createIdAndComplicationData(LEFT_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_RANGED_VALUE,
-                ComplicationData.TYPE_LONG_TEXT,
-                ComplicationData.TYPE_SHORT_TEXT,
-                ComplicationData.TYPE_ICON,
-                ComplicationData.TYPE_SMALL_IMAGE
+            listOf(
+                ComplicationType.RANGED_VALUE,
+                ComplicationType.LONG_TEXT,
+                ComplicationType.SHORT_TEXT,
+                ComplicationType.MONOCHROMATIC_IMAGE,
+                ComplicationType.SMALL_IMAGE
             ),
             DefaultComplicationProviderPolicy(SystemProviders.SUNRISE_SUNSET)
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
             .build()
 
@@ -147,15 +147,15 @@
             ).apply {
                 idAndData = createIdAndComplicationData(RIGHT_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_RANGED_VALUE,
-                ComplicationData.TYPE_LONG_TEXT,
-                ComplicationData.TYPE_SHORT_TEXT,
-                ComplicationData.TYPE_ICON,
-                ComplicationData.TYPE_SMALL_IMAGE
+            listOf(
+                ComplicationType.RANGED_VALUE,
+                ComplicationType.LONG_TEXT,
+                ComplicationType.SHORT_TEXT,
+                ComplicationType.MONOCHROMATIC_IMAGE,
+                ComplicationType.SMALL_IMAGE
             ),
             DefaultComplicationProviderPolicy(SystemProviders.DAY_OF_WEEK)
-        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+        ).setDefaultProviderType(ComplicationType.SHORT_TEXT)
             .setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
             .build()
 
@@ -168,11 +168,11 @@
             ).apply {
                 idAndData = createIdAndComplicationData(BACKGROUND_COMPLICATION_ID)
             },
-            intArrayOf(
-                ComplicationData.TYPE_LARGE_IMAGE
+            listOf(
+                ComplicationType.BACKGROUND_IMAGE
             ),
             DefaultComplicationProviderPolicy()
-        ).setDefaultProviderType(ComplicationData.TYPE_LARGE_IMAGE)
+        ).setDefaultProviderType(ComplicationType.BACKGROUND_IMAGE)
             .setAsBackgroundComplication()
             .build()
 
@@ -289,7 +289,7 @@
 
         verify(fragmentController).showComplicationConfig(
             LEFT_COMPLICATION_ID,
-            *leftComplication.supportedTypes
+            *ComplicationType.toWireTypes(leftComplication.supportedTypes)
         )
     }
 
@@ -299,7 +299,7 @@
 
         verify(fragmentController).showComplicationConfig(
             BACKGROUND_COMPLICATION_ID,
-            *backgroundComplication.supportedTypes
+            *ComplicationType.toWireTypes(backgroundComplication.supportedTypes)
         )
     }
 
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 76987d6..2741333 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -50,11 +50,18 @@
   public final class WindowManager {
     ctor public WindowManager(android.content.Context);
     ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+    method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+    method public androidx.window.WindowMetrics getMaximumWindowMetrics();
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
+  public final class WindowMetrics {
+    ctor public WindowMetrics(android.graphics.Rect);
+    method public android.graphics.Rect getBounds();
+  }
+
 }
 
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 76987d6..2741333 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -50,11 +50,18 @@
   public final class WindowManager {
     ctor public WindowManager(android.content.Context);
     ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+    method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+    method public androidx.window.WindowMetrics getMaximumWindowMetrics();
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
+  public final class WindowMetrics {
+    ctor public WindowMetrics(android.graphics.Rect);
+    method public android.graphics.Rect getBounds();
+  }
+
 }
 
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 76987d6..2741333 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -50,11 +50,18 @@
   public final class WindowManager {
     ctor public WindowManager(android.content.Context);
     ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+    method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+    method public androidx.window.WindowMetrics getMaximumWindowMetrics();
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
+  public final class WindowMetrics {
+    ctor public WindowMetrics(android.graphics.Rect);
+    method public android.graphics.Rect getBounds();
+  }
+
 }
 
diff --git a/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java b/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
index 90d3156..15a069e 100644
--- a/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
+++ b/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
@@ -33,6 +33,7 @@
 class TestWindowBoundsHelper extends WindowBoundsHelper {
     private Rect mGlobalOverriddenBounds;
     private final HashMap<Activity, Rect> mOverriddenBounds = new HashMap<>();
+    private final HashMap<Activity, Rect> mOverriddenMaximumBounds = new HashMap<>();
 
     /**
      * Overrides the bounds returned from this helper for the given context. Passing null {@code
@@ -46,6 +47,14 @@
     }
 
     /**
+     * Overrides the max bounds returned from this helper for the given context. Passing {@code
+     * null} {@code bounds} has the effect of clearing the bounds override.
+     */
+    void setMaximumBoundsForActivity(@NonNull Activity activity, @Nullable Rect bounds) {
+        mOverriddenMaximumBounds.put(activity, bounds);
+    }
+
+    /**
      * Overrides the bounds returned from this helper for all supplied contexts. Passing null
      * {@code bounds} has the effect of clearing the global override.
      */
@@ -68,6 +77,17 @@
         return super.computeCurrentWindowBounds(activity);
     }
 
+    @NonNull
+    @Override
+    Rect computeMaximumWindowBounds(Activity activity) {
+        Rect bounds = mOverriddenMaximumBounds.get(activity);
+        if (bounds != null) {
+            return bounds;
+        }
+
+        return super.computeMaximumWindowBounds(activity);
+    }
+
     /**
      * Clears any overrides set with {@link #setCurrentBounds(Rect)} or
      * {@link #setCurrentBoundsForActivity(Activity, Rect)}.
@@ -75,5 +95,6 @@
     void reset() {
         mGlobalOverriddenBounds = null;
         mOverriddenBounds.clear();
+        mOverriddenMaximumBounds.clear();
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
index 93cfa19..cf6eab3 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
 import android.graphics.Point;
@@ -45,7 +46,9 @@
             new ActivityScenarioRule<>(TestActivity.class);
 
     @Test
-    public void testGetCurrentWindowBounds_matchParentWindowSize_avoidCutouts() {
+    public void testGetCurrentWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
+        assumePlatformBeforeR();
+
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
             assumeFalse(isInMultiWindowMode(activity));
 
@@ -61,7 +64,9 @@
     }
 
     @Test
-    public void testGetCurrentWindowBounds_fixedWindowSize_avoidCutouts() {
+    public void testGetCurrentWindowBounds_fixedWindowSize_avoidCutouts_preR() {
+        assumePlatformBeforeR();
+
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
             assumeFalse(isInMultiWindowMode(activity));
 
@@ -77,7 +82,9 @@
     }
 
     @Test
-    public void testGetCurrentWindowBounds_matchParentWindowSize_layoutBehindCutouts() {
+    public void testGetCurrentWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
+        assumePlatformBeforeR();
+
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
             assumeFalse(isInMultiWindowMode(activity));
 
@@ -93,7 +100,9 @@
     }
 
     @Test
-    public void testGetCurrentWindowBounds_fixedWindowSize_layoutBehindCutouts() {
+    public void testGetCurrentWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
+        assumePlatformBeforeR();
+
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
             assumeFalse(isInMultiWindowMode(activity));
 
@@ -108,22 +117,137 @@
         });
     }
 
+    @Test
+    public void testGetCurrentWindowBounds_postR() {
+        assumePlatformROrAbove();
+
+        runActionsAcrossActivityLifecycle(activity -> { }, activity -> {
+            Rect bounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
+            Rect windowMetricsBounds =
+                    activity.getWindowManager().getCurrentWindowMetrics().getBounds();
+            assertEquals(windowMetricsBounds, bounds);
+        });
+    }
+
+    @Test
+    public void testGetMaximumWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
+        assumePlatformBeforeR();
+
+        testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+            assumeFalse(isInMultiWindowMode(activity));
+
+            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.MATCH_PARENT;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                lp.layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+            }
+            activity.getWindow().setAttributes(lp);
+        });
+    }
+
+    @Test
+    public void testGetMaximumWindowBounds_fixedWindowSize_avoidCutouts_preR() {
+        assumePlatformBeforeR();
+
+        testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+            assumeFalse(isInMultiWindowMode(activity));
+
+            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+            lp.width = 100;
+            lp.height = 100;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                lp.layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+            }
+            activity.getWindow().setAttributes(lp);
+        });
+    }
+
+    @Test
+    public void testGetMaximumWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
+        assumePlatformBeforeR();
+
+        testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+            assumeFalse(isInMultiWindowMode(activity));
+
+            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.MATCH_PARENT;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                lp.layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            }
+            activity.getWindow().setAttributes(lp);
+        });
+    }
+
+    @Test
+    public void testGetMaximumWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
+        assumePlatformBeforeR();
+
+        testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+            assumeFalse(isInMultiWindowMode(activity));
+
+            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+            lp.width = 100;
+            lp.height = 100;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                lp.layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            }
+            activity.getWindow().setAttributes(lp);
+        });
+    }
+
+    @Test
+    public void testGetMaximumWindowBounds_postR() {
+        assumePlatformROrAbove();
+
+        runActionsAcrossActivityLifecycle(activity -> { }, activity -> {
+            Rect bounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+            Rect windowMetricsBounds =
+                    activity.getWindowManager().getMaximumWindowMetrics().getBounds();
+            assertEquals(windowMetricsBounds, bounds);
+        });
+    }
+
     private void testGetCurrentWindowBoundsMatchesRealDisplaySize(
             ActivityScenario.ActivityAction<TestActivity> initialAction) {
+        ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsMatchesDisplayAction =
+                new AssertCurrentWindowBoundsEqualsRealDisplaySizeAction();
+        runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction);
+    }
+
+    private void testGetMaximumWindowBoundsMatchesRealDisplaySize(
+            ActivityScenario.ActivityAction<TestActivity> initialAction) {
+        ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsMatchesDisplayAction =
+                new AssertMaximumWindowBoundsEqualsRealDisplaySizeAction();
+        runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction);
+    }
+
+    /**
+     * Creates and launches an activity performing the supplied actions at various points in the
+     * activity lifecycle.
+     *
+     * @param initialAction the action that will run once before the activity is created.
+     * @param verifyAction the action to run once after each change in activity lifecycle state.
+     */
+    private void runActionsAcrossActivityLifecycle(
+            ActivityScenario.ActivityAction<TestActivity> initialAction,
+            ActivityScenario.ActivityAction<TestActivity> verifyAction) {
         ActivityScenario<TestActivity> scenario = mActivityScenarioRule.getScenario();
         scenario.onActivity(initialAction);
 
-        ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsAction =
-                new AssertWindowBoundsEqualsRealDisplaySizeAction();
-
         scenario.moveToState(Lifecycle.State.CREATED);
-        scenario.onActivity(assertWindowBoundsAction);
+        scenario.onActivity(verifyAction);
 
         scenario.moveToState(Lifecycle.State.STARTED);
-        scenario.onActivity(assertWindowBoundsAction);
+        scenario.onActivity(verifyAction);
 
         scenario.moveToState(Lifecycle.State.RESUMED);
-        scenario.onActivity(assertWindowBoundsAction);
+        scenario.onActivity(verifyAction);
     }
 
     private static boolean isInMultiWindowMode(Activity activity) {
@@ -133,7 +257,15 @@
         return false;
     }
 
-    private static final class AssertWindowBoundsEqualsRealDisplaySizeAction implements
+    private static void assumePlatformBeforeR() {
+        assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.R);
+    }
+
+    private static void assumePlatformROrAbove() {
+        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+    }
+
+    private static final class AssertCurrentWindowBoundsEqualsRealDisplaySizeAction implements
             ActivityScenario.ActivityAction<TestActivity> {
         @Override
         public void perform(TestActivity activity) {
@@ -153,4 +285,25 @@
                     realDisplaySize.y, bounds.height());
         }
     }
+
+    private static final class AssertMaximumWindowBoundsEqualsRealDisplaySizeAction implements
+            ActivityScenario.ActivityAction<TestActivity> {
+        @Override
+        public void perform(TestActivity activity) {
+            Display display;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                display = activity.getDisplay();
+            } else {
+                display = activity.getWindowManager().getDefaultDisplay();
+            }
+
+            Point realDisplaySize = WindowBoundsHelper.getRealSizeForDisplay(display);
+
+            Rect bounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+            assertEquals("Window bounds width does not match real display width",
+                    realDisplaySize.x, bounds.width());
+            assertEquals("Window bounds height does not match real display height",
+                    realDisplaySize.y, bounds.height());
+        }
+    }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
index 957e9cb..dddc9ef 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
@@ -16,12 +16,15 @@
 
 package androidx.window;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
 import android.content.ContextWrapper;
+import android.graphics.Rect;
 
 import androidx.core.util.Consumer;
 import androidx.test.core.app.ApplicationProvider;
@@ -30,6 +33,7 @@
 
 import com.google.common.util.concurrent.MoreExecutors;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,6 +44,11 @@
 @RunWith(AndroidJUnit4.class)
 public final class WindowManagerTest extends WindowTestBase {
 
+    @After
+    public void tearDown() {
+        WindowBoundsHelper.setForTesting(null);
+    }
+
     @Test
     public void testConstructor_activity() {
         new WindowManager(mock(Activity.class), mock(WindowBackend.class));
@@ -89,4 +98,36 @@
         wm.unregisterDeviceStateChangeCallback(consumer);
         verify(backend).unregisterDeviceStateChangeCallback(eq(consumer));
     }
+
+    @Test
+    public void testGetCurrentWindowMetrics() {
+        WindowBackend backend = mock(WindowBackend.class);
+        Activity activity = mock(Activity.class);
+        WindowManager wm = new WindowManager(activity, backend);
+
+        Rect bounds = new Rect(1, 2, 3, 4);
+        TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
+        mWindowBoundsHelper.setCurrentBoundsForActivity(activity, bounds);
+        WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
+
+        WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
+        assertNotNull(windowMetrics);
+        assertEquals(bounds, windowMetrics.getBounds());
+    }
+
+    @Test
+    public void testGetMaximumWindowMetrics() {
+        WindowBackend backend = mock(WindowBackend.class);
+        Activity activity = mock(Activity.class);
+        WindowManager wm = new WindowManager(activity, backend);
+
+        Rect bounds = new Rect(0, 2, 4, 5);
+        TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
+        mWindowBoundsHelper.setMaximumBoundsForActivity(activity, bounds);
+        WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
+
+        WindowMetrics windowMetrics = wm.getMaximumWindowMetrics();
+        assertNotNull(windowMetrics);
+        assertEquals(bounds, windowMetrics.getBounds());
+    }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java b/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java
new file mode 100644
index 0000000..126e31e
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link WindowMetrics} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class WindowMetricsTest {
+
+    @Test
+    public void testGetBounds() {
+        Rect bounds = new Rect(1, 2, 3, 4);
+        WindowMetrics windowMetrics = new WindowMetrics(bounds);
+        assertEquals(bounds, windowMetrics.getBounds());
+    }
+
+    @Test
+    public void testEquals_sameBounds() {
+        Rect bounds = new Rect(1, 2, 3, 4);
+        WindowMetrics windowMetrics0 = new WindowMetrics(bounds);
+        WindowMetrics windowMetrics1 = new WindowMetrics(bounds);
+
+        assertEquals(windowMetrics0, windowMetrics1);
+    }
+
+    @Test
+    public void testEquals_differentBounds() {
+        Rect bounds0 = new Rect(1, 2, 3, 4);
+        WindowMetrics windowMetrics0 = new WindowMetrics(bounds0);
+
+        Rect bounds1 = new Rect(6, 7, 8, 9);
+        WindowMetrics windowMetrics1 = new WindowMetrics(bounds1);
+
+        assertNotEquals(windowMetrics0, windowMetrics1);
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqual() {
+        Rect bounds = new Rect(1, 2, 3, 4);
+        WindowMetrics windowMetrics0 = new WindowMetrics(bounds);
+        WindowMetrics windowMetrics1 = new WindowMetrics(bounds);
+
+        assertEquals(windowMetrics0.hashCode(), windowMetrics1.hashCode());
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/WindowBoundsHelper.java b/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
index 882f30c..d2db428 100644
--- a/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
+++ b/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
@@ -104,14 +104,13 @@
      * <p>
      * Note: The value of this is based on the last windowing state reported to the client.
      *
+     * @see android.view.WindowManager#getCurrentWindowMetrics()
      * @see android.view.WindowMetrics#getBounds()
      */
     @NonNull
     Rect computeCurrentWindowBounds(Activity activity) {
         if (Build.VERSION.SDK_INT >= R) {
-            android.view.WindowManager platformWindowManager =
-                    activity.getSystemService(android.view.WindowManager.class);
-            return platformWindowManager.getCurrentWindowMetrics().getBounds();
+            return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
         } else if (Build.VERSION.SDK_INT >= Q) {
             return computeWindowBoundsQ(activity);
         } else if (Build.VERSION.SDK_INT >= P) {
@@ -123,6 +122,27 @@
         }
     }
 
+    /**
+     * Computes the maximum size and position of the area the window can expect with
+     * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+     * and any combination of flags that would allow the window to extend behind display cutouts.
+     * <p>
+     * The value returned from this method will always match {@link Display#getRealSize(Point)} on
+     * {@link Build.VERSION_CODES#Q Android 10} and below.
+     *
+     * @see android.view.WindowManager#getMaximumWindowMetrics()
+     */
+    @NonNull
+    Rect computeMaximumWindowBounds(Activity activity) {
+        if (Build.VERSION.SDK_INT >= R) {
+            return activity.getWindowManager().getMaximumWindowMetrics().getBounds();
+        } else {
+            Display display = activity.getWindowManager().getDefaultDisplay();
+            Point displaySize = getRealSizeForDisplay(display);
+            return new Rect(0, 0, displaySize.x, displaySize.y);
+        }
+    }
+
     /** Computes the window bounds for {@link Build.VERSION_CODES#Q}. */
     @NonNull
     @RequiresApi(Q)
diff --git a/window/window/src/main/java/androidx/window/WindowManager.java b/window/window/src/main/java/androidx/window/WindowManager.java
index 9304d49..76c3633 100644
--- a/window/window/src/main/java/androidx/window/WindowManager.java
+++ b/window/window/src/main/java/androidx/window/WindowManager.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -107,6 +108,57 @@
     }
 
     /**
+     * Returns the {@link WindowMetrics} according to the current system state.
+     * <p>
+     * The metrics describe the size of the area the window would occupy with
+     * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+     * and any combination of flags that would allow the window to extend behind display cutouts.
+     * <p>
+     * The value of this is based on the <b>current</b> windowing state of the system. For
+     * example, for activities in multi-window mode, the metrics returned are based on the
+     * current bounds that the user has selected for the {@link android.app.Activity Activity}'s
+     * window.
+     *
+     * @see #getMaximumWindowMetrics()
+     * @see android.view.WindowManager#getCurrentWindowMetrics()
+     */
+    @NonNull
+    public WindowMetrics getCurrentWindowMetrics() {
+        Activity activity = getActivityFromContext(mContext);
+        Rect currentBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
+        return new WindowMetrics(currentBounds);
+    }
+
+    /**
+     * Returns the largest {@link WindowMetrics} an app may expect in the current system state.
+     * <p>
+     * The metrics describe the size of the largest potential area the window might occupy with
+     * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+     * and any combination of flags that would allow the window to extend behind display cutouts.
+     * <p>
+     * The value of this is based on the largest <b>potential</b> windowing state of the system.
+     * For example, for activities in multi-window mode the metrics returned are based on what the
+     * bounds would be if the user expanded the window to cover the entire screen.
+     * <p>
+     * Note that this might still be smaller than the size of the physical display if certain
+     * areas of the display are not available to windows created for the associated {@link Context}.
+     * For example, devices with foldable displays that wrap around the enclosure may split the
+     * physical display into different regions, one for the front and one for the back, each acting
+     * as different logical displays. In this case {@link #getMaximumWindowMetrics()} would return
+     * the region describing the side of the device the associated {@link Context context's}
+     * window is placed.
+     *
+     * @see #getCurrentWindowMetrics()
+     * @see android.view.WindowManager#getMaximumWindowMetrics()
+     */
+    @NonNull
+    public WindowMetrics getMaximumWindowMetrics() {
+        Activity activity = getActivityFromContext(mContext);
+        Rect maxBounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+        return new WindowMetrics(maxBounds);
+    }
+
+    /**
      * Unwraps the hierarchy of {@link ContextWrapper}-s until {@link Activity} is reached.
      * @return Base {@link Activity} context or {@code null} if not available.
      */
diff --git a/window/window/src/main/java/androidx/window/WindowMetrics.java b/window/window/src/main/java/androidx/window/WindowMetrics.java
new file mode 100644
index 0000000..9faa5f6
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/WindowMetrics.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Metrics about a {@link android.view.Window}, consisting of its bounds.
+ * <p>
+ * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} or
+ * {@link WindowManager#getMaximumWindowMetrics()}.
+ *
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+public final class WindowMetrics {
+    @NonNull
+    private final Rect mBounds;
+
+    /**
+     * Constructs a new {@link WindowMetrics} instance.
+     *
+     * @param bounds rect describing the bounds of the window, see {@link #getBounds}.
+     */
+    public WindowMetrics(@NonNull Rect bounds) {
+        mBounds = new Rect(bounds);
+    }
+
+    /**
+     * Returns a new {@link Rect} describing the bounds of the area the window occupies.
+     * <p>
+     * <b>Note that the size of the reported bounds can have different size than
+     * {@link Display#getSize(Point)}.</b> This method reports the window size including all system
+     * decorations, while {@link Display#getSize(Point)} reports the area excluding navigation bars
+     * and display cutout areas.
+     *
+     * @return window bounds in pixels.
+     */
+    @NonNull
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        WindowMetrics that = (WindowMetrics) o;
+        return mBounds.equals(that.mBounds);
+    }
+
+    @Override
+    public int hashCode() {
+        return mBounds.hashCode();
+    }
+}
diff --git a/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml b/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml
index f48b579..1f0eff0 100644
--- a/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml
+++ b/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml
@@ -20,6 +20,7 @@
 
     <!-- Important: disable debuggable for accurate performance results -->
     <application
+        android:requestLegacyExternalStorage="true"
         android:debuggable="false"
         tools:replace="android:debuggable">
         <!-- enable profileableByShell for non-intrusive profiling tools -->